Jeg har et nyt projekt, jeg arbejder på, hvor jeg vil have et Oracle-job til at tilbagekalde privilegier, jeg har givet til it-medarbejdere, der er ældre end 30 dage. Vores it-medarbejdere har brug for lejlighedsvis adgang til nogle få produktionstabeller for at fejlfinde problemer. Vi giver SELECT privs på bordene, den person har brug for, men ingen fortæller mig nogensinde, hvornår de er færdige med deres opgave, og disse privilegier sidder derude for evigt. Jeg ville have et system til automatisk at tilbagekalde privilegier, der er ældre end 30 dage, så jeg ikke skulle huske at gøre det. Før jeg kunne tilbagekalde privilegier, havde jeg brug for en måde at spore disse privilegier på. Så jeg oprettede en trigger, der udløses, når der udstedes et GRANT, og logger detaljerne til en tabel. Senere vil et Oracle-job scanne den tabel og tilbagekalde privilegier, det finder, som er for gamle. Min triggerkode er som følger:
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; /
Ovenstående kode er ikke original. Jeg fandt et godt eksempel på internettet og ændrede et par ting. Efter at have testet koden i 3 uger, rullede jeg aftrækkeren i produktion. Det tog kun et par dage for mig at modtage en fejl.
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...Jeg opretter en bruger, der ikke giver noget. Men vi kan med sikkerhed se, at min trigger har et problem med at udføre. Så hvorfor udløses denne trigger, hvis alt, hvad jeg gør, er at oprette en bruger? En simpel SQL-sporing viste mig, hvad der foregik med den rekursive SQL. Bag kulisserne udsender Oracle følgende på mine vegne:
GIV ARV PRIVILEGIER TIL BRUGER "BOB" til OFFENTLIG;
Ok...så på dette tidspunkt ved jeg, at der udstedes et GRANT, når jeg opretter en bruger, men hvorfor mislykkes dette? Jeg testede denne trigger med systemrettigheder, og det fungerede fint. Indrømmet, jeg testede ikke ARVE PRIVILEGIER, så det her er en slags kantsag.
Efter en god mængde fejlfindingsindsats fandt jeg ud af, at ora_privilege_list funktionskaldet returnerer et tomt sæt til samlingen med navnet "priv". Som sådan bliver npriv sat til en NULL-værdi. Fordi NPRIV er NULL, giver linjen hvor der står "for i in 1..npriv" ikke meget mening, derfor fejlen.
Efter min mening bør ora_privilege_list returnere ét element, "ARV PRIVILEGES", og jeg mener, at det ikke returnerer denne liste til at være en fejl. Men hvis ora_privilege_list kommer til at returnere en tom samling, så burde outputtet fra funktionen være nul, og så ville npriv få en mere korrekt værdi. Til uddannelsesformål er ora_privilege_list et synonym for DBMS_STANDARD.PRIVILEGE_LIST.
Når det er sagt, kan jeg ikke kontrollere Oracle-funktionen. Og jeg vil ikke vente på, at Oracle ændrer deres kode i DBMS_STANDARD til, hvad jeg synes, den burde være. Så jeg vil bare kode min trigger for at håndtere problemet. Tilføjelse af to enkle linjer løste mit problem (se nedenfor med fed skrift).
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; /
Så rettelsen er ret enkel. Udfør kun de to FOR-løkker, hvis NPRIV ikke er nul.