Multitenant, erste Schritte, Umgebung konfigurieren

04.
Oktober
2022
Veröffentlicht von: Holger Eekhoff

Zwar ist bereits seit der Version 12 (2013) die Multitenant-Architektur eingeführt worden, diese hat allerdings bei dem ein oder andern Kunden bislang keine Beachtung gefunden. Mit der Oracle Version 21 wird man aber nicht mehr umhin kommen diese Architektur umzusetzen, da ab dann keine non-CDB Datenbanken mehr erstellt werden können. In diesem Monatstipp möchte ich kurz auf die ersten Schritte eingehen die notwendig sind, nachdem eine Pluggable Database (PDB) erstellt wurde, wie man sich als DBA an einer PDB anmelden kann und wie man sich die Umgebung für eine PDB unter Linux einrichten kann.

Multitenant-Architektur

Die Multitenant-Architektur umschreibt eine mandantenfähige Datenbank, die ihren Memory, die Hintergrundprozesse usw. einer oder mehreren Pluggable Databases zur Verfügung stellt.
In den Pluggable Databases liegen abgekapselt die Applikationsdaten, so können sich z. B. eine Entwicklungs-, eine Test- und eine Integrations-PDB die SGA usw. einer CDB teilen.
Durch Einsatz dieser Architektur kann eine Konsolidierung von Datenbanken erfolgen. Doch sowohl Konsolidierung als auch Mandantenfähigkeit ließen sich auch in der alten Archietektur mit geringem Aufwand realisieren. Über die Sinnhaftigkeit bzw. den Overhead beim Einsatz einer einzelnen Datenbank kann man allerdings streiten.

Nomenkaltur

Non-CDB: eine herkömmlich angelegte Datenbank (ohne Einsatz von Multitenant ab Version 12)

Container Datenbank (CDB): Container Datenbank in die eine oder mehrere Pluggable Databases eingeklinkt werden können. Die Container Datenbank stellt den PDBs den Memory (SGA), die Hintergrundprozesse, Kontrolldatei(en) und die Redolog-Dateien zur Verfügung.

Pluggable Database (PDB): In der Pluggable Database liegt mehr oder weniger nur noch der Applikationsteil der Datenbank, sprich Applikationsuser und Tablespaces mit Applikationsdaten. 
 

Erste Schritte

In den folgenden Beispielen heißt die CDB : CDBA mit den RAC Instanzen CDBA1 und CDBA2
In der CDBA liegen die beiden die PDBs PDBEINS und PDBZWEI. Bei den PDBs gibt es im RAC keinen eigenen Instanznamen. Unter Linux werden die Umgebungsvariablen ORACLE_SID=CDBA1 (erste RAC Instanz) sowie das ORACLE_HOME gesetzt und sqlplus aufgerufen:
An die CDB kann man sich analog einer non-CDB z. B. mit „sqlplus / as sysdba“ anmelden.
Nachdem die erste PDB mit dem DBCA oder per Skript angelegt oder eine non-CDB z. B. mit dem Autoupgrade-Tool in eine PDB umgewandelt wurde, ist die Frage welche PDBs laufen in der CDB und wie melde ich mich dort an.
Für die Abfrage von Multitenant Informationen gibt es diverse CDB_* (Erweiterung der DBA-Views um die CON_ID) ein paar PDB_* Views, sowie V_$PDBS bzw. GV$PDBS.

Hier zunächst die Anzeige der eingerichteten PDBs samt Status:

[]$ . CDBA1

ORACLE_SID:      CDBA1
ORACLE_HOME:     /u01/app/oracle/product/19.0.0.0/dbhome_1

[CDBA1]$ sqlplus / as sysdba

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDBEINS                        MOUNTED
         4 PDBZWEI                        MOUNTED

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        MOUNTED
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        MOUNTED

6 rows selected.

Wie wir sehen können, sind sowohl PDBEINS als auch PDBZWEI nach der Erstellung nicht geöffnet. Die Oracle interne PDB PDB$SEED ist normalerweise nur lesend geöffnet.

Starten und Stoppen von PDBs

Im Folgenden wird aufgezeigt, wie man eine PDB öffnet, bzw. wie man im RAC-Umfeld eine PDB auf einer oder allen Instanzen öffnet:

SQL> -- oeffnen der PDB in der aktuellen Instanz (CDBA1):
SQL> alter pluggable database PDBEINS open;

Pluggable database altered.

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        MOUNTED

6 rows selected.

SQL> -- oeffnen der PDB in einer bestimmten Instanz (CDBA2):
SQL> alter pluggable database PDBZWEI open instances=('CDBA2');
SQL> -- oeffnen der PDB in allen Instanzen:
SQL> alter pluggable database PDBZWEI open instances=all;

Pluggable database altered.

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        READ WRITE
         2          4 PDBZWEI                        READ WRITE

6 rows selected.

Nach einem Restart der Datenbank ist im Gegensatz zur CDB die PDB jedoch immer noch geschlossen (bzw. im MOUNTED Status). Ausnahme, eine Grid Infrastructure Umgebung incl. einem Service für die PDB ist eingerichtet (siehe weiter unten). Ändern lässt sich dies über den „save state":

[CDBA1]$ srvctl stop  database -d cdba
[CDBA1]$ srvctl start database -d cdba

[CDBA1]$ sqlplus / as sysdba

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        MOUNTED
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        MOUNTED

6 rows selected.

SQL> alter pluggable database PDBEINS OPEN;

Pluggable database altered.

SQL> -- Sichern des „Open“ Status:
SQL> alter pluggable database PDBEINS save state;

Pluggable database altered.

SQL> -- Abfrage des „save state“
SQL> select CON_NAME, INSTANCE_NAME, STATE from dba_pdb_saved_states;

CON_NAME                       INSTANCE_NAME                  STATE
------------------------------ ------------------------------ --------------
PDBEINS                        CDBA1                          OPEN

SQL> -- Auch wenn jetzt der save state für alle Instanzen gesetzt wird,
SQL> -- wird dies nicht vermerkt, da die PDBEINS auf CDBA2 nicht geoeffnet ist:
SQL> alter pluggable database PDBEINS save state instances=all;

Pluggable database altered.

SQL> select CON_NAME, INSTANCE_NAME, STATE from dba_pdb_saved_states;

CON_NAME                       INSTANCE_NAME                  STATE
------------------------------ ------------------------------ --------------
PDBEINS                        CDBA1                          OPEN

SQL> -- oeffnet man alle Instanzen,
SQL> alter pluggable database PDBZWEI open instances=all;

Pluggable database altered.

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        READ WRITE
         2          4 PDBZWEI                        READ WRITE

6 rows selected.

SQL> -- und setzt den „save state“ nur für eine Instanz, so wird nach dem
SQL> -- reboot ebenfalls nur auf einer Instanz die PDBZWEI geoeffnet:
SQL> alter pluggable database PDBZWEI  save state instances=('CDBA2');

Pluggable database altered.

SQL> select CON_NAME, INSTANCE_NAME, STATE  from  dba_pdb_saved_states;

CON_NAME                       INSTANCE_NAME                  STATE
------------------------------ ------------------------------ --------------
PDBEINS                        CDBA1                          OPEN
PDBZWEI                        CDBA2                          OPEN

SQL> exit

[CDBA1]$ srvctl stop database -d CDBA
[CDBA1]$ srvctl start database -d CDBA

[CDBA1]$ sqlplus / as sysdba

SQL> -- jetzt sind die PDBs auf je einer Instanz geoeffnet
SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        READ WRITE

6 rows selected.

SQL> select CON_NAME, INSTANCE_NAME, STATE from dba_pdb_saved_states;

CON_NAME                       INSTANCE_NAME                  STATE
------------------------------ ------------------------------ --------------
PDBZWEI                        CDBA2                          OPEN
PDBEINS                        CDBA1                          OPEN

Wie wir gesehen haben, muss die PDB geöffnet sein, wenn der „save state“ gesetzt wird, damit die PDB nach einem Restart der CDB automatisch mitgestartet wird.

Ein Zurücknehmen des „save state“ Status kann auf zwei Arten erfolgen, entweder man stoppt die PDB und sichert den „save state“ oder man löscht den Eintrag mit „discard“:

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        READ WRITE

6 rows selected.

SQL> alter pluggable database PDBEINS close instances=all;

Pluggable database altered.

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        MOUNTED
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        READ WRITE

6 rows selected.


SQL> select CON_NAME, INSTANCE_NAME, STATE from dba_pdb_saved_states;

CON_NAME                       INSTANCE_NAME                  STATE
------------------------------ ------------------------------ ----
PDBZWEI                        CDBA2                          OPEN
PDBEINS                        CDBA1                          OPEN

SQL> alter pluggable database PDBEINS save state  instances=all;

Pluggable database altered.

SQL> select CON_NAME, INSTANCE_NAME, STATE  from  dba_pdb_saved_states;

CON_NAME                       INSTANCE_NAME                  STATE
------------------------------ ------------------------------ --------------
PDBZWEI                        CDBA2                          OPEN

SQL> alter pluggable database PDBZWEI discard state;

Pluggable database altered.

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        MOUNTED
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        READ WRITE

6 rows selected.

SQL> --Ein discard in der akt. Instanz bewirkt in diesem Falle nichts, da für die PDBZWEI
SQL> --für die zweite Instanz ein „save state“ gesetzt ist.  
SQL> select CON_NAME, INSTANCE_NAME, STATE from dba_pdb_saved_states;

CON_NAME                       INSTANCE_NAME                  STATE
------------------------------ ------------------------------ --------------
PDBZWEI                        CDBA2                          OPEN

SQL> alter pluggable database PDBZWEI discard state instances=all;

Pluggable database altered.

SQL> select CON_NAME, INSTANCE_NAME, STATE from dba_pdb_saved_states;

no rows selected

SQL> quit


Nach einem Restart der CDB werden die PDBs dann nicht mehr mit gestartet:

[CDBA1]$ srvctl stop database -d CDBA
[CDBA1]$ srvctl start database -d CDBA

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1;

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        MOUNTED
         2          3 PDBEINS                        MOUNTED
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        MOUNTED

6 rows selected.

SQL> quit


Nutzt man einen Service der über die Grid Infrastructure angelegt und gestartet wird, so werden die „save state“ Einträge zum Starten der PDB übersteuert und sind hinfällig:

[CDBA1]$ srvctl enable service -d CDBA -s client_pdbeins
[CDBA1]$ srvctl start  service -d CDBA -s client_pdbeins


SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        READ WRITE
         1          4PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        MOUNTED

6 rows selected.

SQL> select CON_NAME, INSTANCE_NAME, STATE  from  dba_pdb_saved_states;

no rows selected

SQL> quit

[CDBA1]$ crsctl status res -t | grep -A2 pdb

ora.cdba.client_pdbeins.svc
      1        ONLINE  ONLINE       tl19hawe03               STABLE
      2        ONLINE  ONLINE       tl19hawe04               STABLE
ora.cdba.client_pdbzwei.svc
      1        OFFLINE OFFLINE                               STABLE
      2        OFFLINE OFFLINE                               STABLE


Achtung!    
Beim Stoppen des Service wird die PDB nicht mit gestoppt. Startet man die PDB über SQL-Plus, so wird der Grid Infrastructure Service nicht mit gestartet.

[CDBA1]$ srvctl stop service -d CDBA -s client_pdbeins

[CDBA1]$ srvctl enable service -d CDBA -s client_pdbzwei

[CDBA1]$ crsctl status res -t | grep -A2 pdb
ora.cdba.client_pdbeins.svc
      1        OFFLINE OFFLINE                               STABLE
      2        OFFLINE OFFLINE                               STABLE
ora.cdba.client_pdbzwei.svc
      1        ONLINE  OFFLINE      tl19hawe03               STARTING
      2        ONLINE  OFFLINE      tl19hawe04               STARTING

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        READ WRITE
         1          4 PDBZWEI                        MOUNTED
         2          4 PDBZWEI                        MOUNTED

6 rows selected.

SQL> alter pluggable database PDBZWEI OPEN instances=all;

SQL> select INST_ID, CON_ID, NAME, OPEN_MODE from gv$pdbs order by 2,1

   INST_ID     CON_ID NAME                           OPEN_MODE
---------- ---------- ------------------------------ ----------
         1          2 PDB$SEED                       READ ONLY
         2          2 PDB$SEED                       READ ONLY
         1          3 PDBEINS                        READ WRITE
         2          3 PDBEINS                        READ WRITE
         1          4 PDBZWEI                        READ WRITE
         2          4 PDBZWEI                        READ WRITE

6 rows selected.

[CDBA1]$ crsctl status res -t | grep -A2 pdb
ora.cdba.client_pdbeins.svc
      1        OFFLINE OFFLINE                               STABLE
      2        OFFLINE OFFLINE                               STABLE
ora.cdba.client_pdbzwei.svc
      1        ONLINE  OFFLINE      tl19hawe03               STARTING
      2        ONLINE  OFFLINE      tl19hawe04               STARTING


Meine Empfehlung:    
Den „Save State“ immer für alle Instanzen setzen.
Den „Save State“ auch setzen, wenn ein Service für die PDBs angelegt wird.
Starten einer PDB durch Starten des Service.
Stoppen einer PDB in SQL-Plus mit:  alter pluggable database … CLOSE …

Wechsel in SQLPLUS von der CDB zur PDB und zurück

Sofern man mit „sysdba“ Rechten angemeldet ist, kann ein Wechsel zwischen CDB und PDB über „alter session set container=…“ erfolgen:

[CDBA1]$ sqlplus / as sysdba

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDBEINS                        READ WRITE NO
         4 PDBZWEI                        MOUNTED

SQL> col USER_NAME form a20
col CDB_NAME form a20
col CONTAINER form a20
col SERVICE_NAME  form a30
select sys_context ('USERENV','SESSION_USER') USER_NAME,
       sys_context ('USERENV','CDB_NAME') CDB_NAME,
       sys_context ('USERENV','CON_NAME') CONTAINER,
       sys_context ('USERENV','SERVICE_NAME') SERVICE_NAME from dual;
 
USER_NAME            CDB_NAME             CONTAINER            SERVICE_NAME
-------------------- -------------------- -------------------- --------------------
SYS                  CDBA                 CDB$ROOT             SYS$USERS

SQL> alter session set container=PDBZWEI;

SQL> select sys_context ('USERENV','SESSION_USER') USER_NAME,
       sys_context ('USERENV','CDB_NAME') CDB_NAME,
       sys_context ('USERENV','CON_NAME') CONTAINER,
       sys_context ('USERENV','SERVICE_NAME') SERVICE_NAME from dual;

USER_NAME            CDB_NAME             CONTAINER            SERVICE_NAME
-------------------- -------------------- -------------------- --------------------
SYS                  CDBA                 PDBZWEI              SYS$USERS

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         4 PDBZWEI                        MOUNTED
        
SQL> alter session set container=CDB$ROOT;

USER_NAME            CDB_NAME             CONTAINER            SERVICE_NAME
-------------------- -------------------- -------------------- --------------------
SYS                  CDBA                 CDB$ROOT             SYS$USERS

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDBEINS                        READ WRITE NO
         4 PDBZWEI                        MOUNTED
SQL>


Anmelden direkt an einer PDB

Alle Benutzer ohne „sysdba“ Rechte haben nur die Möglichkeit sich über eine SQL-Net Verbindung mit der PDB zu verbinden. Dazu wird pro PDB ein TNS-Alias in der tnsnames.ora angelegt:

[CDBA1]$ cat tnsnames.ora

. . .
PDBEINS_CON =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = tcp)(HOST = rac19c-scan.muniqsoft.de)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = client_pdbeins)
    )
  )
. . .

[CDBA1]$ sqlplus system@PDBEINS_CON
Enter password:
Connected.

select sys_context ('USERENV','SESSION_USER') USER_NAME,
       sys_context ('USERENV','CDB_NAME') CDB_NAME,
       sys_context ('USERENV','CON_NAME') CONTAINER,
       sys_context ('USERENV','SERVICE_NAME') SERVICE_NAME from dual;

USER_NAME            CDB_NAME             CONTAINER            SERVICE_NAME
-------------------- -------------------- -------------------- -----------------
SYSTEM               CDBA                 PDBEINS              client_pdbeins

SQL> create user test identified by manager;

User created.

SQL> grant connect to test;

Grant succeeded.


SQL> connect test/manager;
ERROR:
ORA-01017: invalid username/password; logon denied



SQL> connect test/manager@PDBEINS_CON
Connected.


USER_NAME            CDB_NAME             CONTAINER            SERVICE_NAME
-------------------- -------------------- -------------------- ------------------
TEST                 CDBA                 PDBEINS              client_pdbeins


Achtung!    
Bei einer direkten Anmeldung in SQL-Plus als nicht „sysdba“ Benutzer an einer CDB/PDB ohne SQL-Net erhält man einen ORA-01017 Fehler

Umgebung für PDB unter Linux

Oracle bietet ab der Version 18 bzw. 19 incl. April 2019 RU mit der undokumentierten Umgebungs-variablen ORACLE_PDB_SID und dem Trigger DBMS_SET_PDB auch die Möglichkeit als Benutzer mit sysdba Rechten sich „direkt“ an einer PDB anzumelden, siehe unter anderem Blog von Mike Dietrich:

https://mikedietrichde.com/2020/05/07/can-you-connect-as-sysdba-without-password-into-a-pdb-directly
https://mikedietrichde.com/2020/07/13/pitfalls-connect-to-a-pdb-directly-with-oracle_pdb_sid/

und der Oracle Support Artikel:

Performing bequeath direct connections to PDB as SYS and SYSTEM (Doc ID 2728684.1)

Dies ist jedoch mit Vorsicht zu genießen, da der von Oracle ausgelieferte Trigger derzeit keinerlei Exception Handling bietet. Sprich gibt es keine PDB mit dem Namen, der über die Variable gesetzt wird, so kann der Trigger kein „alter session set container = …“ durchführen und man landet in der CDB statt der gewünschten PDB.

Dies kann aber mit zwei/drei „Kniffen“ so gut wie vermieden werden:

  1. Die gesamte Umgebung bzw. Variablen ORACLE_SID und ORACLE_PDB_SID usw. sollten nur über zentrale Skripten gesetzt werden. ORACLE_PDB_SID muss dabei immer zurückgesetzt werden, wenn es sich um keine PDB handelt. Die DBAs sollten fortan die Umgebung bzw. Variablen nicht mehr von Hand setzen, sondern die definierten Skripte verwenden.
  2. Anlegen eines login.sql/glogin.sql Skript, dass beim Anmelden in SQL-Plus automatisch aufgerufen wird und das Informationen zur Anmeldung ausgibt und ggf. auch im Prompt anzeigt, ob man an einer PDB angemeldet ist oder nicht
  3. Anpassen des Triggers DBMS_SET_PDB der mittels Exception Handling und/oder mittels DBMS_OUTPUT anzeigt, ob/wie/wo man sich angemeldet hat oder ob ein Fehler bei der Anmeldung auftrat. Eine Anpassung des Triggers birgt aber die Gefahr, dass Oracle bei einem zukünftigen Update diese Änderungen überschreibt.

Beispiel Skript zum Setzen der Umgebung für PDBEINS:

cat /home/oracle/PDBEINS

# set Environment for PDB : PDBEINS
export ORACLE_DBNAME="PDBEINS"
export ORACLE_SID="CDBA1"
# new variable to set environment for PDB
export ORACLE_PDB_SID="PDBEINS"
export ORACLE_BASE=/u01/app/oracle
export ORACLE_HOME=/u01/app/oracle/product/19.0.0.0/dbhome_1
# Set ORACLE_PATH to location of login.sql script
export ORACLE_PATH=${ORACLE_BASE}/sql
export PATH=${ORACLE_HOME}/bin:${PATH}
export PS1='[${ORACLE_PDB_SID:-${ORACLE_SID} }]\$ '  
         
echo "ORACLE_DBNAME:   ${ORACLE_DBNAME}"
echo "ORACLE_SID:      ${ORACLE_SID}"
echo "ORACLE_PDB_SID:  ${ORACLE_PDB_SID}"
echo "ORACLE_HOME:     ${ORACLE_HOME}"

Bei allen nicht PDB-Skripten wird dann ORACLE_PDB_SID="" gesetzt!

Beispiel für ein login.sql Skript:

set serveroutput on format WRAPPED size 1000000
set trimspool on
set long 5000
set linesize 100
set feedback off
declare
  v_prompt         VARCHAR2(40);
  v_user_name      VARCHAR2(20);
  v_db_name        VARCHAR2(9);
  v_db_unique_name VARCHAR2(30);
  v_container      VARCHAR2(30);
  v_cdb            VARCHAR2(3);
  v_cdb_pdb        VARCHAR2(3);
  v_cdb_name       VARCHAR2(30);
  v_con_id         NUMBER;
  v_instance       VARCHAR2(30);
  v_host           VARCHAR2(30);
  v_service        VARCHAR2(30);
begin
  -- get infos via sys_context
  v_user_name:=substr(sys_context ('USERENV','SESSION_USER'),1,20);
  v_db_name:=sys_context ('USERENV','DB_NAME');
  v_db_unique_name:=sys_context ('USERENV','DB_UNIQUE_NAME');
  v_con_id:=sys_context ('USERENV','CON_ID');
  v_cdb_name:=sys_context ('USERENV','CDB_NAME');
  v_container:=sys_context ('USERENV','CON_NAME');
  v_instance:=sys_context ('USERENV','INSTANCE_NAME');
  v_host:=sys_context ('USERENV','HOST');
  v_service:=sys_context ('USERENV','SERVICE_NAME');
  if nvl(v_con_id,0)=0 then
     v_cdb_pdb:='';
  elsif v_con_id=1 then
     v_cdb_pdb:='CDB';
  else
     v_cdb_pdb:='PDB';
  end if;
  -- display the information
  dbms_output.put_line(chr(13) || rpad ('   #', 84, '#'));
  if v_cdb_pdb is not null then
    dbms_output.put_line('   # ' || rpad ('Container:  '||v_container,37,' ')   
      || ' # ' || rpad ('CDB/PDB:      ' || v_cdb_pdb,37,' ') || ' #') ;
    dbms_output.put_line('   # ' || rpad ('CDB-Name:   '||v_cdb_name,37,' ')    
      || ' # ' || rpad ('DB-Uniq-Name: ' || v_db_unique_name,37,' ') || ' #') ;
  else
    dbms_output.put_line('   # ' || rpad ('DB-Name:    '||v_db_name,37,' ')     
      || ' # ' || rpad ('DB-Uniq-Name: ' || v_db_unique_name,37,' ') || ' #') ;
  end if;
    dbms_output.put_line('   # ' || rpad ('Host:       '||v_host,37,' ')        
      || ' # ' || rpad ('Service:      ' || v_service,37,' ') || ' #') ;
  dbms_output.put_line(rpad ('   #', 84, '#') || (chr(13));
exception
  WHEN OTHERS THEN
     dbms_output.put_line( 'when others' || sqlerrm);
end;
/
set termout off
-- setting the prompt depending on CDB/PDB Type
define gprompt='SQL>'
column global_prompt new_value gprompt
select decode (nvl(sys_context ('USERENV','CON_ID'),0)
               , '0',  substr(sys_context ('USERENV','SESSION_USER'),1,20)
                         ||'@'||sys_context ('USERENV','INSTANCE_NAME')
               , '1', 'CDB:'||substr(sys_context ('USERENV','SESSION_USER'),1,20)
                         ||'@'||sys_context ('USERENV','INSTANCE_NAME')
                         ||':'||sys_context ('USERENV','CON_NAME')
               ,      'PDB:'||substr(sys_context ('USERENV','SESSION_USER'),1,20)
                         ||'@'||sys_context ('USERENV','CON_NAME')) global_prompt
  from dual;
set sqlprompt '&gprompt> '
set termout on
undefine gprompt
column global_prompt clear

Mit obigen Skripten setzen wir die Umgebung einmal für die PDB PDBEINS und einmal für die CDB-Datenbank CDBA und rufen sqlplus auf:

[]$ . PDBEINS

ORACLE_DBNAME:   PDBEINS
ORACLE_SID:      CDBA1
ORACLE_PDB_SID:  PDBEINS
ORACLE_HOME:     /u01/app/oracle/product/19.0.0.0/dbhome_1

[PDBEINS]$ sqlplus / as sysdba

   #################################################################################
   # Container:  PDBEINS                   # CDB/PDB:      PDB                     #
   # CDB-Name:   CDBA                      # DB-Uniq-Name: CDBA                    #
   # Host:       racnode1.muniqsoft.de     # Service:      client_pdbeins          #
   #################################################################################

PDB:SYS@PDBEINS>

PDB:SYS@PDBEINS> quit

[PDBEINS]$ . CDBA1

ORACLE_DBNAME:   CDBA
ORACLE_SID:      CDBA1
ORACLE_PDB_SID:  
ORACLE_HOME:     /u01/app/oracle/product/19.0.0.0/dbhome_1

[CDBA1]$ sqlplus / as sysdba

   #################################################################################
   # Container:  CDB$ROOT                  # CDB/PDB:      CDB                     #
   # CDB-Name:   CDBA                      # DB-Uniq-Name: CDBA                    #
   # Host:       racnode1.muniqsoft.de     # Service:      SYS$USERS               #
   #################################################################################

CDB:SYS@CDBA1:CDB$ROOT>


Achtung!    
Bei einem Wechsel des Kontextes mit „alter session set container=…“ innerhalb von SQL-Plus wird login.sql nicht automatisch aufgerufen und somit bleibt dabei der Prompt unverändert. Um den Prompt anzupassen, muss das login.sql von Hand aufrufen werden. Alternativ meldet man sich ohne Setzen des Containers mit Hilfe eines TNS-Alias gleich an der richtigen PDB an, dann wird automatisch das login.sql Skript ausgeführt.

CDB:SYS@CDBA1:CDB$ROOT> alter session set container=PDBZWEI;

Session altered.

CDB:SYS@CDBA1:CDB$ROOT> @login.sql

   #################################################################################
   # Container:  PDBZWEI                   # CDB/PDB:      PDB                     #
   # CDB-Name:   CDBA                      # DB-Uniq-Name: CDBA                    #
   # Host:       racnode1.muniqsoft.de     # Service:      client_pdbzwei          #
   #################################################################################

PDB:SYS@PDBZWEI>

PDB:SYS@PDBZWEI> connect system/manager@PDBEINS_CON
Connected.

   #################################################################################
   # Container:  PDBEINS                   # CDB/PDB:      PDB                     #
   # CDB-Name:   CDBA                      # DB-Uniq-Name: CDBA                    #
   # Host:       racnode1.muniqsoft.de     # Service:      client_pdbeins          #
   #################################################################################

PDB:SYSTEM@PDBEINS>


Zusammenfassung

Der Architekturwandel bei Oracle ist leider unumgänglich. Somit müssen im täglichen Ablauf, in vorhandenem Scripting und Monitoring teils erhebliche Aufwände betrieben werden, um weiterhin einen reibungslosen Ablauf zu gewährleisten.

To be continued.

Quellen

Oracle Dokumentation z. B.:

https://docs.oracle.com/en/database/oracle/oracle-database/19/multi/administering-a-cdb-with-sql-plus.html#GUID-2E5025EE-BED3-45A4-9EAA-B05C8DA66F97

Blog von Mike Dietrich

https://mikedietrichde.com/

Jede Menge Know-how für Sie!

In unserer Know-How Datenbank finden Sie mehr als 300 ausführliche Beiträge zu den Oracle-Themen wie DBA, SQL, PL/SQL, APEX und vielem mehr.
Hier erhalten Sie Antworten auf Ihre Fragen.