Wer sind eigentlich meine Datenbank-Clients?

By | 13. Mai 2018

Client-Erfassung mit einem Trigger

Einführung

Die Datenbanklandschaften in vielen Organisationen sind heutzutage sehr komplex, heterogen und vielfältig. Sie sind gekennzeichnet durch eine große Anzahl von Datenbanken, viele gemischte Anwendungen und diverse Schnittstellen. Nicht selten existiert ein ganzer Zoo von unterschiedlichen Clients. Eine Übersicht über die Client-Vielfalt einer Datenbank, hilft dabei ein grundsätzliches Verständnis über die jeweiligen Anwendungen und das Nutzungsprofil einer Datenbank zu erlangen.

Database Clients

Aufgrund der Schnelllebigkeit von IT-Anwendungen und den damit verbundenen stetigen Änderungenprozessen, findet man in den seltensten Fällen eine umfassende und aktuelle Dokumentation aller Clients einer Datenbank im Unternehmen. In diesem Blog-Beitrag möchte ich die Zweckmäßigkeit einer solchen Client-Übersicht aufzeigen und eine Möglichkeit vorstellen, wie ein DBA sich eine solche mit Hilfe eines Logon-Triggers erstellen kann.

Nutzen einer Client-Übersicht

Für den Oracle DBA ist es mitunter sehr hilfreich zu wissen, welche Datenbank-Clients sich wie und von wo aus mit einer Datenbank verbinden. Dies gilt insbesondere dann, wenn geplante Änderungen, wie z.B. Release-Upgrades, Server-Migrationen, Netzwerk-Modifikationen oder Konsolidierungen anstehen. Zum Zweck der Risikominimierung muss der verantwortliche DBA einschätzen können, welche Auswirkung eine geplante Änderung auf die Verfügbarkeit einer Anwendung oder den Datenbankbetrieb hat. Je höher der Vernetzungsgrad zwischen Datenbanken und Clients, desto größer ist das Risiko, dass eine Änderung in dem Umfeld negative Auswirkungen nach sich zieht. Nicht selten wird mal schnell ein alter Batchjob oder eine selten genutzte Schnittstelle vergessen, wenn kein ausreichender Überblick existiert.

Folgende typische Fragen gilt es beispielsweise im Rahmen von Changes zu beantworten.

  • Laufen meine Clients noch zukünftig mit dem neuen Release der Datenbank?
  • Auf welche Anwendungs- oder Betriebskomponente wirkt sich ein Ausfall eines Datenbank-Services aus?
  • Von welchem Host greift ein bestimmter Client auf die Datenbank zu?
  • Bei welchen Clients müssen die Verbindungsinformationen angepasst werden, wenn Datenbank-Host, Service-Name oder Port sich ändert?
  • Auf welche Benutzer wirkt sich eine Änderung der Datenbank aus?
  • Gibt es Verbindungen, die von einer Remote-Datenbank über einen Datenbank-Link hergestellt werden?

Bei der Suche nach Fehlerursachen im Client-Umfeld ist es gleichermaßen sachdienlich, wenn der DBA genaue Informationen über die Clients hat.

  • Mit welchem Zeichensatz- und welchen Spracheinstellungen verbindet sich ein Client mit der Datenbank?
  • Welcher Client-Treiber wird von einem bestimmten Client genutzt?
  • Welchen Servicenamen nutzt eine Anwendung?
  • Mit welcher ‚NLS Sort‘-Einstellung wird eine Clientverbindung aufgebaut?
  • Gibt es Clients, die sich ungewöhnlich oft (im Sekundentakt) an die Datenbank anmelden?
  • Mit welcher RAC-Instanz verbinden sich bestimmte Clients wie häufig?

Auch wenn eine Client-Übersicht kein Audit ersetzen soll, so kann es zur Erhöhung der Zugriffs- und Datensicherheit dienen und Schwachstellen aufdecken.

  • Welche Clients verbinden sich mit SYSDBA-Berechtigung?
  • Welche User haben sich schon sehr lange nicht mehr an die Datenbank angemeldet?
  • Welcher Client nutzt welche Authentifizierungsmethode?
  • Gibt es Anwendungen oder User, die eigentlich keinen Zugriff mehr haben sollten?
  • Welcher Client verbindet sich via SQL*Net und mit welchem Protokoll?

Wer ist aktuell wie mit der Datenbank verbunden?

Mit folgender Abfrage auf die beiden Views V$SESSION und V$SESSION_CONNECT_INFO lassen sich bereits einige Informationen über die aktuell mit der Datenbank verbundenen Clients ermitteln.

set pagesize 200
set linesize 130
col username FOR a10
col client_version FOR a10
col osuser FOR a13
col machine FOR a17
col MODULE FOR a30
col program FOR a16
col client_driver FOR a13
col service_name FOR a12
SELECT b.username,
       a.client_version,
       a.osuser,
       b.machine,
       b.MODULE,
       b.program,
       a.client_driver,
       b.service_name
FROM   gv$session_connect_info a,
       gv$session b
WHERE  a.sid = b.sid
       AND a.serial# = b.serial#
       AND b.MODULE <> 'DBMS_SCHEDULER'
       AND network_service_banner LIKE 'TCP%'
ORDER  BY client_version,
          username;

Beispiel Output:

USERNAME   CLIENT_VER OSUSER   MACHINE     MODULE                    CLIENT_DRIVER SERVICE_NAME
--------- ---------- --------- ----------- ------------------------- ------------- ------------
APPL_PHH  11.2.0.2.0 tomcat    tet-bt5     JDBC Thin Client          jdbcthin     db1
APPL_DED  11.2.0.3.0 tomcat    tet-bt5     JDBC Thin Client          jdbcthin     odb1
LADME     11.2.0.4.0 oracle    oemsrv      sqlldr@oemsrv TNS V1-V3)  null         odb1
DAN       unknown    osafe     oserv1      JDBC Thin Client          jdbcthin     odb1
DAM       unknown    osafe     oserv1      sqlplus@oserv1(TNS V1-V3) jdbcthin     odb1
DBSNMP    11.2.0.3.0 oracle    oserv1      perl@oserv1               null         SYS$USERS
MANAGER   12.1.0.2.0 activemq  tet-jms-t1  JDBC Thin Client          jdbcthin     odb1
OUSER_TK  11.2.0.3.0 tomcat    eventpc     JDBC Thin Client          jdbcthin     odb1
RIO_PAR   11.2.0.4.0 jboss     appserv1    JDBC Thin Client          jdbcthin     odb1_rio
RIO_PAR   11.2.0.4.0 jboss     appserv1    JDBC Thin Client          jdbcthin     odb1_rio
SYS       11.2.0.3.0 oracle    oserv1      oraagent.bin@oserv1       null         SYS$USERS
SYS       11.2.0.3.0 oracle    oserv1      sqlplus@oserv1(TNS V1-V3) SQL*PLUS     SYS$USERS
SYSTEM    11.2.0.4.0 oracle    oemsrv      OMS                       jdbcthin     odb1

Der Nachteil dieser Abfrage ist, dass man immer nur ein Abbild der Client-Situation zum Zeitpunkt der Abfrage erhält und man dementsprechend nie eine lückenlose Client-Übersicht über einen längeren Zeitraum bekommt. Zu groß ist die Gefahr, dass wichtige Client-Verbindungen unentdeckt bleiben. Um diesen Mangel zu beheben, empfehle ich hier eine Logon-Trigger-basierte Lösung, wie im nachfolgenden Abschnitt genauer beschrieben wird, zu erstellen.

Client-Logon-Trigger

Da sich, spätestens nach einem Neustart der Datenbank, jeder Client mindestens einmal mit der Datenbank verbindet, macht es Sinn diese Daten mit Hilfe eines „After Logon“-Triggers zu erfassen und in eine Tabelle abzuspeichern. Neben den Informationen, welche die View V$SESSION_CONNECT_INFO bereits mitbringt, ist es empfehlenswert auch die jeweiligen Session-Parameter mit der Funktion SYS_CONTEXT abzufragen, da sie einen zusätzlichen nützlichen Informationsgehalt bieten.

Nachfolgend möchte ich ein Beispiel eines solchen Logon-Triggers vorstellen. Der Trigger CLIENT_LOGON_AL_TRG erfasst jede Anmeldung einer Session und schreibt die Informationen, die die Session mit sich bringt, in die Tabelle CLIENT_LOGONS. Vorausgesetzt der Trigger ist lange genug aktiv, beinhaltet die Tabelle folglich eine umfassende Übersicht aller Clients der Datenbank, die sich nach Belieben auswerten lässt. Ob man den Trigger nur für einen repräsentativen Zeitraum aktiviert oder die Client-Anmeldungen dauerhaft protokolliert, bleibt jedem DBA selbst überlassen.

Um nicht für jeden Verbindungsaufbau einen neuen Datensatz zu generieren, wird jede gleichartige Client-Verbindung nur einmal erfasst. Meldet sich eine gleichartige Session ein weiteres Mal an, so wird der Zeitpunkt erfasst und ein Zähler erhöht. Dadurch lässt sich der Zeitpunkt der ersten und der letzten Anmeldung ermitteln und außerdem herausfinden, wie häuft sich ein Client angemeldet hat. Ändert sich ein Merkmal einer Session, so wird ein neuer Datensatz erzeugt. Diese Eigenschaft unterscheidet den von mir entwickelten Logon-Trigger von den meisten anderen Logon-Triggern dieser Art. Die Anzahl der Datensätze bleibt in einem überschaubaren Rahmen und der Überblick geht nicht verloren.

In meinem u.a. Beispiel wird die Tabelle in dem Schema SYSTEM erstellt. Wenn dies nicht erwünscht ist, kann man die Tabelle auch in jedem beliebigen anderen Schema erstellen. Wichtig ist hierbei nur, das der Eigentüner des Logon-Triggers in diese Tabelle schreiben darf.

Erstellung Tabelle CLIENT_LOGONS:

CREATE TABLE SYSTEM.client_logons
  (
     username             VARCHAR2(100),
     login_count          NUMBER,
     first_login          DATE,
     last_login           DATE,
     client_version       VARCHAR2(100),
     proxy_user           VARCHAR2(100),
     client_osuser        VARCHAR2(100),
     client_host          VARCHAR2(100),
     client_ip            VARCHAR2(15),
     client_module        VARCHAR2(100),
     client_info          VARCHAR2(100),
     client_action        VARCHAR2(100),
     network_protocol     VARCHAR2(100),
     terminal             VARCHAR2(30),
     dblink_source        VARCHAR2(100),
     authent_method       VARCHAR2(50),
     authent_identy       VARCHAR2(50),
     authent_type         VARCHAR2(50),
     ident_type           VARCHAR2(50),
     session_nls_language VARCHAR2(50),
     session_nls_sort     VARCHAR2(50),
     is_sysdba            VARCHAR2(30),
     oci_library          VARCHAR2(50),
     client_charset       VARCHAR2(50),
     client_driver        VARCHAR2(50),
     connection_flag      VARCHAR2(50),
     service_name         VARCHAR2(100),
     server_host          VARCHAR2(100),
     instance_no          VARCHAR2(50),
     instance_name        VARCHAR2(50)
  )
TABLESPACE users; 

CREATE INDEX SYSTEM.client_logons_idx_username
  ON SYSTEM.client_logons (username);

 

Erstellung Trigger SYS.CLIENT_LOGONS_AL_TRG:

CREATE OR REPLACE TRIGGER sys.client_logons_al_trg
AFTER LOGON ON DATABASE
BEGIN
INSERT INTO system.client_logons
SELECT
nvl(sys_context('USERENV', 'SESSION_USER'),'null') username,
0 login_count,
sysdate first_login,
sysdate last_login,
a.client_version client_version,
nvl(sys_context('USERENV', 'PROXY_USER'),'null') proxy_user,
nvl(sys_context('USERENV', 'OS_USER'),'null') client_osuser,
nvl(sys_context('USERENV', 'HOST'),'null') client_host,
nvl(sys_context('USERENV','IP_ADDRESS', 15),'null') client_ip,
nvl(sys_context('USERENV', 'MODULE'),'null') client_module,
nvl(sys_context('USERENV', 'CLIENT_INFO'),'null') client_info,
nvl(sys_context('USERENV', 'ACTION'),'null') client_action,
nvl(sys_context('USERENV', 'NETWORK_PROTOCOL'),'null') network_protocol,
nvl(sys_context('USERENV', 'TERMINAL'),'null') terminal,
nvl(substr(sys_context('USERENV', 'DBLINK_INFO'), 1, instr(sys_context('USERENV', 'DBLINK_INFO'), '.') -1),'null') dblink_source,
nvl(sys_context('USERENV', 'AUTHENTICATION_METHOD'),'null') authent_method,
nvl(sys_context('USERENV', 'AUTHENTICATED_IDENTITY'),'null') authent_identy,
a.authentication_type authent_type,
nvl(sys_context('USERENV', 'IDENTIFICATION_TYPE'),'null') ident_type,
nvl(sys_context('USERENV', 'LANGUAGE'),'null') session_nls_language,
nvl(sys_context('USERENV', 'NLS_SORT'),'null') session_nls_sort,
sys_context('USERENV', 'ISDBA') is_sysdba,
a.client_oci_library oci_library,
a.client_charset client_charset,
nvl(a.client_driver,'null') client_driver,
a.client_connection connection_flag,
nvl(sys_context('USERENV', 'SERVICE_NAME'),'null') service_name,
nvl(sys_context('USERENV', 'SERVER_HOST'),'null') server_host,
nvl(sys_context('USERENV', 'INSTANCE'),'null') instance_no,
nvl(sys_context('USERENV', 'INSTANCE_NAME'),'null') instance_name 
FROM v$session_connect_info a
WHERE a.sid = sys_context('USERENV', 'SID')
AND nvl(sys_context('USERENV', 'AUTHENTICATION_METHOD'),'null') != 'JOBS'
AND (a.network_service_banner like '%TCP%' OR a.network_service_banner like 'Oracle Bequeath%')
AND NOT EXISTS 
(SELECT NULL
FROM system.client_logons b
WHERE b.username = nvl(sys_context('USERENV', 'SESSION_USER'),'null')
AND b.client_version = a.client_version
AND b.proxy_user = nvl(sys_context('USERENV', 'PROXY_USER'),'null') 
AND b.client_osuser = nvl(sys_context('USERENV', 'OS_USER'),'null')
AND b.client_host = nvl(sys_context('USERENV', 'HOST') ,'null')
AND b.client_ip = nvl(sys_context('USERENV','IP_ADDRESS', 15),'null')
AND b.client_module = nvl(sys_context('USERENV', 'MODULE'),'null')
AND b.client_info = nvl(sys_context('USERENV', 'CLIENT_INFO'),'null') 
AND b.client_action = nvl(sys_context('USERENV', 'ACTION'),'null')
AND b.network_protocol = nvl(sys_context('USERENV', 'NETWORK_PROTOCOL'),'null')
AND b.terminal = nvl(sys_context('USERENV', 'TERMINAL'),'null')
AND b.dblink_source = nvl(substr(sys_context('USERENV', 'DBLINK_INFO'), 1, instr(sys_context('USERENV', 'DBLINK_INFO'), '.') -1),'null')
AND b.authent_method = nvl(sys_context('USERENV', 'AUTHENTICATION_METHOD'),'null') 
AND b.authent_identy = nvl(sys_context('USERENV', 'AUTHENTICATED_IDENTITY'),'null')
AND b.authent_type = a.authentication_type
AND b.ident_type = nvl(sys_context('USERENV', 'IDENTIFICATION_TYPE'),'null')
AND b.session_nls_language = nvl(sys_context('USERENV', 'LANGUAGE'),'null')
AND b.session_nls_sort = nvl(sys_context('USERENV', 'NLS_SORT'),'null')
AND b.is_sysdba = sys_context('USERENV', 'ISDBA')
AND b.oci_library = a.client_oci_library 
AND b.client_charset = a.client_charset
AND b.client_driver = nvl(a.client_driver,'null')
AND b.connection_flag = a.client_connection
AND b.service_name = nvl(sys_context('USERENV', 'SERVICE_NAME'),'null')
AND b.server_host = nvl(sys_context('USERENV', 'SERVER_HOST'),'null')
AND b.instance_no = nvl(sys_context('USERENV', 'INSTANCE'),'null')
AND b.instance_name = nvl(sys_context('USERENV', 'INSTANCE_NAME'),'null')
);
UPDATE system.client_logons b
SET b.last_login = sysdate,
b.login_count = b.login_count+1
WHERE
client_osuser = nvl(sys_context('USERENV', 'OS_USER'),'null')
AND b.username = nvl(sys_context('USERENV', 'SESSION_USER'),'null')
AND b.proxy_user = nvl(sys_context('USERENV', 'PROXY_USER'),'null')
AND b.client_host = nvl(sys_context('USERENV', 'HOST'),'null')
AND b.client_ip = nvl(sys_context('USERENV','IP_ADDRESS', 15),'null')
AND b.client_module = nvl(sys_context('USERENV', 'MODULE'),'null')
AND b.client_info = nvl(sys_context('USERENV', 'CLIENT_INFO'),'null') 
AND b.client_action = nvl(sys_context('USERENV', 'ACTION'),'null')
AND b.network_protocol = nvl(sys_context('USERENV', 'NETWORK_PROTOCOL'),'null')
AND b.network_protocol = nvl(sys_context('USERENV', 'NETWORK_PROTOCOL'),'null')
AND b.terminal = nvl(sys_context('USERENV', 'TERMINAL'),'null')
AND b.dblink_source = nvl(substr(sys_context('USERENV', 'DBLINK_INFO'), 1, instr(sys_context('USERENV', 'DBLINK_INFO'), '.') -1),'null')
AND b.authent_method = nvl(sys_context('USERENV', 'AUTHENTICATION_METHOD'),'null')
AND b.authent_identy = nvl(sys_context('USERENV', 'AUTHENTICATED_IDENTITY'),'null')
AND b.ident_type = nvl(sys_context('USERENV', 'IDENTIFICATION_TYPE'),'null')
AND b.session_nls_language = nvl(sys_context('USERENV', 'LANGUAGE'),'null')
AND b.session_nls_sort = nvl(sys_context('USERENV', 'NLS_SORT'),'null')
AND b.is_sysdba = sys_context('USERENV', 'ISDBA')
AND b.service_name = nvl(sys_context('USERENV', 'SERVICE_NAME'),'null')
AND b.server_host = nvl(sys_context('USERENV', 'SERVER_HOST'),'null')
AND b.instance_no = nvl(sys_context('USERENV', 'INSTANCE'),'null') 
AND b.instance_name = nvl(sys_context('USERENV', 'INSTANCE_NAME'),'null')
AND b.client_module = nvl(sys_context('USERENV', 'MODULE'),'null')
AND b.client_version||b.authent_type||b.oci_library||b.client_charset||b.client_driver||b.connection_flag = 
(SELECT a.client_version||a.authentication_type||a.client_oci_library||a.client_charset||nvl(a.client_driver,'null')||a.client_connection
FROM v$session_connect_info a
WHERE a.sid = sys_context('USERENV', 'SID')
AND nvl(sys_context('USERENV', 'AUTHENTICATION_METHOD'),'null') != 'JOBS'
AND (a.network_service_banner like '%TCP%' OR a.network_service_banner like 'Oracle Bequeath%'));
EXCEPTION
WHEN OTHERS THEN
RETURN;
END;
/

 

Der Trigger wird in dem Beispiel als Benutzer SYS erstellt, da die Info, ob ein User mit der Berechtigung SYSDBA angemeldet wurde (Spalte IS_DBA), nur preisgegeben wird, wenn der Eigentümer des Triggers selbst die SYSBA Berechtigung besitzt. Grundsätzlich kann auch ein anderer Benutzer zur Erstellung des Triggers verwendet werden, solange dieser SYSDBA-Berechtigung hat. Wenn dies nicht möglich ist und man auf die Informationen in der Spalte IS_DBA verzichten kann, dann kann man auch einen Nicht-SYSDBA Benutzer verwenden. Allerdings sollte man dann dafür sorgen, dass der Benutzer zumindest das Recht ADMINISTER DATABASE TRIGGER besitzt.

Bei Datenbanken mit normalem Logon-Verhalten der Anwendungen, sind übrigens keine spürbaren Auswirkungen auf die Anwendungsperformance zu erwarten. Bei Anwendungen mit einem ungewöhnlich hohem Logon-Aufkommen im Sekundentakt und einer großen Anzahl verschiedenartiger Verbindungen, sollte man den Trigger vorsichtig einsetzen und zuvor auf negative Auswirkungen hin überprüfen.

Bei der Entwicklung des Triggers stand für mich in erster Linie die Funktion im Vordergrund. Ich will daher nicht ausschließen, dass es an der einen oder anderen Stelle Optimierungspotenzial beim SQL gibt. Verbesserungsvorschläge sind daher willkommen.

Sollen keine Verbindungen mit dem Trigger mehr erfasst werden, so kann man diesen deaktivieren.

ALTER TRIGGER sys.client_logons_al_trg DISABLE;

Exkludieren bestimmer User

Sollen bestimmte Benutzer nicht durch den Trigger protokolliert werden, so lässt sich dieser wie folgt im Code exkludieren:

CREATE OR REPLACE TRIGGER sys.client_logons_al_trg AFTER LOGON ON DATABASE
WHEN (USER != 'SCOTT')
  BEGIN
    INSERT INTO system.client_logons
...

Multitenant Datenbanken

In einer Multitenant Datenbank müssen der Trigger und die Tabelle in der CDB und jeder PDB angelegt werden, will man alle Verbindungen erfassen. Um die Auswertung zu vereinfachen, kann man ab 12.1.0.2 die CONTAINERS Tabellenfunktion nutzen. Diese Funktion ermöglicht es, dass man eine einzige View in der CDB über alle CLIENT_LOGONS Tabellen der CDB und PDBs legen kann. Die Auswertung der Daten kann man dann über diese View durchführen. Die Verwendung der CONTAINERS Funktion setzt allerdings voraus, dass die Tabelle CLIENT_LOGONS in jeder existierenden PDB vorhanden ist.

CREATE VIEW system.all_client_logons
AS
  SELECT *
  FROM   Containers("system"."CLIENT_LOGONS");

Physical Standby Datenbanken

In einer Physical Standby Konfiguration sollte der Trigger nur in die Tabelle schreiben, wenn die Datenbank die Rolle Primary hat. In der Rolle Standby ist die Datenbank nicht geöffnet und folglich kann in die Tabelle nichts geschireben werden. Der Trigger muss also um ein IF-Konstrukt erweitert werden.

CREATE OR replace TRIGGER sys.client_logons_al_trg AFTER logon ON DATABASE
DECLARE
  ROLE VARCHAR(30);
BEGIN
SELECT database_role INTO ROLE FROM v$database; 
  IF ROLE = 'PRIMARY' THEN
    INSERT INTO SYSTEM.client_logons ...
    OR          a.network_service_banner LIKE 'Oracle Bequeath%') );
   ...
   END IF;
EXCEPTION
WHEN OTHERS THEN
  RETURN;
END;
/

Weitere Infos: MOS Note 785885.1 Creating Logon Trigger On Primary Database We Get Errors On Physical Standby ORA-604 ORA-1552

Bug Datapump und Release 11.2.0.3

Bei Release 11.2.0.3 gibt es einen Bug im Bezug auf Datapump Exports, die nicht mir diesem Logon Trigger laufen. Es wird eine ORA-07445 generiert und der Export Job nicht ausgeführt!
MOS Note: Datapump Export Fails With ORA-07445 [nszgclient()]

Auswertung der Tabelle system.client_logons

Die mit dem Trigger erfassten Client-Verbindungen können mit dem unten aufgeführten Statement abgefragt werden. Für gewöhnlich wird man sich zunächst alle Spalten der Tabelle anzeigen lassen wollen, um einen besseren Überblick über die Clients zu gewinnen. Ich lege hier die Verwendung eines grafischen Tools nahe, welches eine benutzerfreundliche Darstellung der 30 Spalten untersützt (z:B, SQL Developer). SQL*Plus ist hier sehr begrenzt, was die Darstellungsbreite angeht.

SELECT username,
       login_count,
       To_char(first_login, 'DD.MM:YYYY HH24:MI') FIRST_LOGIN,
       To_char(last_login, 'DD.MM:YYYY HH24:MI')  LAST_LOGIN,
       client_version,
       proxy_user,
       client_osuser,
       client_host,
       client_ip,
       client_module,
       client_info,
       client_action,
       network_protocol,
       terminal,
       dblink_source,
       authent_method,
       authent_identy,
       authent_type,
       ident_type,
       session_nls_language,
       session_nls_sort,
       is_sysdba,
       oci_library,
       client_charset,
       client_driver,
       connection_flag,
       service_name,
       server_host,
       instance_no,
       instance_name
FROM   SYSTEM.client_logons
ORDER  BY username;

Referenz

V$SESSION_CONNECT_INFO
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/refrn/V-SESSION_CONNECT_INFO.html

SYS_CONTEXT
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/SYS_CONTEXT.html

CREATE TRIGGER
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/CREATE-TRIGGER-statement.htm

CONTAINERS FUNCTION
https://docs.oracle.com/database/121/ADMIN/cdb_mon.htm#ADMIN14319

 

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.