sql >> Databáze >  >> RDS >> Oracle

ORA-6502 se spouštěčem protokolování Grant

Mám nový projekt, na kterém pracuji, kde chci, aby úloha Oracle odebrala oprávnění, která jsem udělil IT zaměstnancům starším než 30 dnů. Naši pracovníci IT potřebují příležitostný přístup k několika produkčním tabulkám, aby mohli řešit problémy. Poskytujeme SELECT priv na stolech, které daný člověk potřebuje, ale nikdo mi nikdy neřekne, kdy skončí se svým úkolem a tato privilegia tam zůstanou navždy. Chtěl jsem, aby systém automaticky odebral oprávnění starší než 30 dní, abych si to nemusel pamatovat. Než jsem mohl zrušit oprávnění, potřeboval jsem způsob, jak tato oprávnění sledovat. Vytvořil jsem tedy spouštěč, který se spustí vždy, když je vydán GRANT, a zaznamená podrobnosti do tabulky. Později úloha Oracle naskenuje tuto tabulku a odebere oprávnění, která zjistí, že jsou příliš stará. Můj spouštěcí kód je následující:

create or replace trigger sys.grant_logging_trig after grant on database
  declare
    priv  dbms_standard.ora_name_list_t;
    who   dbms_standard.ora_name_list_t;
    npriv pls_integer;
    nwho  pls_integer;
  begin
    npriv := ora_privilege_list(priv);
    if (ora_sysevent = 'GRANT') then
      nwho := ora_grantee(who);
    else
      nwho := ora_revokee(who);
    end if;
     for i in 1..npriv
     loop
       for j in 1..nwho
       loop  
        insert into system.grant_logging values
          ( systimestamp,
            ora_login_user,
            ora_sysevent,
            who(j),
            priv(i),
            ora_dict_obj_owner,
            ora_dict_obj_name
          );
      end loop;
    end loop; 
end;
 / 

Výše uvedený kód není originální. Na internetu jsem našel dobrý příklad a pár věcí upravil. Po testování kódu po dobu 3 týdnů jsem spoušť uvedl do výroby. Trvalo jen několik dní, než jsem obdržel chybu.

SQL> CREATE USER bob IDENTIFIED BY password;

ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-04088: error during execution of trigger 'SYS.GRANT_LOGGING_TRIG'
ORA-00604: error occurred at recursive SQL level 2
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at line 28

Hmmm...vytvářím uživatele, který nic neuděluje. Ale můžeme si být jisti, že můj spouštěč má problém se spuštěním. Proč se tedy tento spouštěč spouští, když vše, co dělám, je vytvoření uživatele? Jednoduché sledování SQL mi ukázalo, co se děje s tím rekurzivním SQL. V zákulisí vydává Oracle mým jménem následující:

UDĚLEJTE VEŘEJNOST DĚDITELNÁ PRIVILEGIU UŽIVATELE „BOB“;

Dobře…takže v tuto chvíli vím, že když vytvořím uživatele, je vydán GRANT, ale proč to selhává? Testoval jsem tento spouštěč se systémovými oprávněními a fungovalo to dobře. Je pravda, že jsem netestoval DĚDIT PRIVILEGES, takže je to trochu okrajový případ.

Po značném úsilí při ladění jsem zjistil, že volání funkce ora_privilege_list vrací prázdnou sadu do kolekce s názvem „priv“. Jako takový je npriv nastaven na hodnotu NULL. Protože NPRIV je NULL, řádek, kde je uvedeno „pro i v 1..npriv“, nedává moc smysl, proto došlo k chybě.

Podle mého názoru by měl ora_privilege_list vrátit jednu položku, „ZDĚDIT PRIVILEGES“ a věřím, že nevracení tohoto seznamu je chyba. Pokud však ora_privilege_list bude vracet prázdnou kolekci, pak by výstup z funkce měl být nula a pak by npriv získal správnější hodnotu. Pro účely vzdělávání je ora_privilege_list synonymem pro DBMS_STANDARD.PRIVILEGE_LIST.

Vše, co bylo řečeno, nemohu ovládat funkci Oracle. A nechci čekat, až Oracle změní svůj kód v DBMS_STANDARD na to, co si myslím, že by měl být. Takže jen nakóduji svůj spouštěč, abych problém vyřešil. Přidání dvou jednoduchých řádků vyřešilo můj problém (viz níže tučně).

create or replace trigger sys.grant_logging_trig after grant on database
  declare
    priv  dbms_standard.ora_name_list_t;
    who   dbms_standard.ora_name_list_t;
    npriv pls_integer;
    nwho  pls_integer;
  begin
    npriv := ora_privilege_list(priv);
    if (ora_sysevent = 'GRANT') then
      nwho := ora_grantee(who);
    else
      nwho := ora_revokee(who);
    end if;
   if to_char(npriv) is not null then 
     for i in 1..npriv
     loop
       for j in 1..nwho
       loop  
        insert into system.grant_logging values
          ( systimestamp,
            ora_login_user,
            ora_sysevent,
            who(j),
            priv(i),
            ora_dict_obj_owner,
            ora_dict_obj_name
          );
      end loop;
    end loop; 
  end if;
end;
 / 

Takže oprava je docela jednoduchá. Proveďte pouze dvě smyčky FOR, pokud NPRIV není null.


  1. chyba:'Nelze se připojit k místnímu serveru MySQL přes soket '/var/run/mysqld/mysqld.sock' (2)' -- Chybí /var/run/mysqld/mysqld.sock

  2. Jak identifikovat problémy s výkonem PostgreSQL pomocí pomalých dotazů

  3. Monitorování Percona XtraDB Cluster – klíčové metriky

  4. Oracle text escapující se složenými závorkami a zástupnými znaky