Oracle RAC 11.2 on Windows 2008 R2 – Part I

Tuesday, 31.07.2012 – Noctua4u

DO NOT… IN PRODUCTION!


Obzirom da su mi se više puta obraćali, sa raznih strana sveta, za mišljenje i iskustvo u radu Oracle RAC-a 11.2.0.2 pod Windows 2008 R2 evo sistematizovanih činjenica.

Zapravo, ovo je kratki info i instrukcije o upotrebi RAC-a u produkciji. Sve što je napisano je plod ličnog iskustva i uopšte ne mora da znači da bi se ponovo reprodukovalo ako bi neko ponovo pokušao to isto. Ovo je samo skup činjenica o kojima bi trebalo voditi računa ukoliko neko planira uvođenje ovakvog sistema u produkciju (pod sličnim okruženjem).

Dakle,

Prirpeme za prelazak na RAC su trajale duže vreme. Isprobavali smo svašta nešto i pokušavali razne scenarije. Na kraju smo skupili svo znanje, informacije i hrabrost i krenuli u migraciju. Naravno, kao i kod svakog dobrog Marfijevog zakona, ono što nismo nikada naišli u testiranju, pojavilo se odmah po pokretanju produkcije!

HW i SW

Hardver kojim smo raspolagali u trenutku zvanične “produkcije” je, u to vreme (januar 2011), bio na zavidnom nivou. Bez zalaženja u tehničke detalje, radi se o dva identična servera (HP ProLiant familija) sa 4 procesora od po 4 jezgra. Memorija je po 52GB po nodu. Prostora na storage-u više nego dovoljno.

Problemi pre

Testiranje je obavljeno pod Windows 2008 R2 i Real Application Cluster Oracle 11.1.0.1.  (very last updates u tom momentu i za OS i za DB) – U daljem tekstu Win i Test RAC.
Instalacija je pokušana školski, na osnovu uputstva sa Support-a (u daljem tekstu MetaLink) . Međutim…

Instalacija puca, tj. nije moguće instalirati ClusterWare jer postoji BUG u instalaciji!

Zapravo, ne bug, već je ispusten jedan ključan korak u svim uputstvima, u setovanju Win-a koji izaziva krah i prekid rada installera, pa samim tim i propast celokupne instalacije.

Naravno, to nigde nije dokumentovano niti je, u tom momentu, bilo prijavljeno na Metalinku. Obzirom na to da smo uredno registrovani, legalizovani, plaćamo pozamašne svote i da su ubeđivanja oko naše migracije na RAC bili dugi i obilni, kolege iz Oracle Serbia su zalegli i pronašli rešenje za mali problemčić!
Uz njihove insajderske informacije, uspela je instalacija. (Inače, instalacija ko instalacija, ništa vredno pomena…)

OCFS problemi sa priviledijama

Dalje, naš scenario je bio, obzirom da imamo nešto malo pisanja iz DB po disku (neki nestandardni exporti u fajl i slično), da “između” dva noda postoji jedan shared disk sa nekim mrežnim fajl sistemom. Normalno i prirodno, odluka je pala na OCFS. Instalacija, formatiranje i podešavanje tog “zajedničkog” diska nije problem. Međutim…
Prilikom testiranja konstatujemo jedan Win limit!

Naime, Win NE MOŽE da barata ACL listama na OCFS-u!!!

U prevodu na govorni srpski: Win nema podršku za prava pristupa fajlovima na OCFS prilikom share nekog direktorijuma!
Dakle, situacija je sledeća: ili su svi korisnici mrežni administratori i mogu po defaultu sve ili nema pristupa uopšte.

Hvala i doviđenja!

Obzirom da prvi scenario nije bio prihvatljiv u našem okruženju, a drugi nije zadovoljavao potrebe, rešavamo problem metodom zakrpe: Problematične aplikacije se konektuju samo na jedan nod (!) na kome je fizički zakačen HDD koji je NTFS formatiran. Normalno, da ne bi bilo čudnih alerta i grešaka u samom RACu, morali smo da zakačimo i za drugi nod jedan disk… tek da glumi prisustvo shared diska…
No, dobro… znam… glupo… ali tako je moralo…
U međuvremenu, prerađene su adekvatne aplikacije tako da umeju da “izbace” fajl na neku mrežnu lokaciju, i… vratili smo se prvobitnom scenariju. Može se reći problem rešen.

Veoma bitno za dalju priču je i činjenica da nismo imali redundantne serverske mrežne kartice, u trenutku kretanja u produkciju. (Za one koji nisu imali prilike da vide iste, razlikuju se od “običnih”… ako ništa drugo ono po tome što imaju mogućnost da se ubodu dva mrežna kabla i da se… da ne dužim, nije bitno za priču. Bitno je da su različite i da ih nismo imali u dovoljnom broju u tom trenutku).

UPGRADE RACa NE RADI pod Win-om!

Kada je trebalo ući u konačnu produkciju, rešio sam da probam nove mehanizme o kojima je Oracle toliko pričao. Naravno, sve je teklo uz pomoć “Master Note For Oracle Database Upgrades and Migrations” MOS ID 1152016.1. Rešio sam da probam “out of place upgrade” sa 11.1 na 11.2. (Btw, sada je upgrade zapravo klasična instalacija). Međutim…

Iako sam pripremio i uradio sve kako piše po njihovim dokumentima, javila se… GREŠKA! Update Clusterware-a nije prošao. Do DB nisam ni stigao…
Žao mi je što nisam sačuvao tadašnje logove i greške koje sam dobijao, šta da se radi…
Probao ja i rollback (uputstvo tipa: šta ako pođe nešto naopako prilikom update-a)… za divno čudo to radi… tj… hmmm… Ne znam koliko kvalitetno radi jer nakon toga nisam pokušavao tesiranje a nije bilo nikakvih poruka o greškama…

Sledeće je bilo, kad sam već zabrljao, da probam “inplace update”.
Naravno da… to pa tek nije htelo da radi!

Problemi posle

Normalno, obzirom da je sve bilo pripremljeno, sledio je format c:/ pa krenimo iznova na verziju 11.2.0.2 (u daljem tekstu RAC) koja je u međuvremenu zašla i “kao” postala stabilna.

Instalacija ovog puta prolazi bez problema jer, poučen iskustvom koristim “trik” iz prethodne priče. Migraciju obavljamo lagano jednog vikenda, januara 2011 godne.

RAC is up and ready.

Normalno, dan posle, počinjem da primećujem neke stvari koje na testiranju nisu bile uočene.

VIRTUAL CIRCUIT WAIT – Network event

Kada se konektuje veći broj korisnika na DB, pojavi se dramatično trošenje resursa u vidu network event -> virtual circuit wait. Ali baš dramatično. Nešto tipa, od fizičkih 16 jezgara, “trošimo” na taj event 120 jezgara!
Prevedeno na govorni srpski, problema nema, sve radi, samo što je periodično usporeno do bola.

Pokušavam razne stvari, analize koda, aplikacije, setovanja, i svega ostalog što bi moglo uroditi plodom, međutim… ništa pametno. Metalink je beskoristan po tom pitanju, u tom trenutku… odgovori i dokumenti su tipa: “To svašta nešto može da znači… kontaktiraj support”… Ajde?
Na rešenje nailazim sasvim slučajno, posle nekoliko meseci kopanja i rešavanja drugih problema… na nekom totalno nebitnom (po oracle) forumu gde tražim rešenje za problem Win update-a.

Naime, radi se o registrovanom Oracle Bug-u za koji su napisali da je razrešen u verziji 11.2 za Win!!!
Bug se ogleda u tome da se problemi javljaju ukoliko se na RAC-u koriste shared konekcije.
Rešenje je samo u tome da se koristi dedicated konekcija!!!!

Plaćam danak svom neiskustvu u radu sa RAC-om.
Takođe, plaćam danak svom, naivnom verovanju da Oracle kad napiše da je rešeno, onda je rešeno…
Moja greška, šta da se radi…

Sada gledam baš na Metalinku, postoji gomila sličnih bugova (U međuvremenu se 11.2 RAC odomaćio pa se javlja i kod drugih sličan problem… Najpribližnije je sledeći, tekući bug 10264920).

WIN Update

HUGE PROBLEM, što bi rekli na maternjem engleskom.
Naime, podučen iskustvom automackog update-a Windows-a (kada je preko noći došao i primenjen je update gde se menja ODBC Connect String), zabranio sam automatiku.
Dragi serveri, obavestite me o update-u, pa da vidimo šta sa tim… Međutim…
Iako su ljudi iz MSofta bili ubedljivi i uverljivi u objašnjavanju da broj pristiglih update-ova (a neinstaliranih) nema nikakav uticaj na rad, ja sam stekao neki subjektivni osećaj da to nije baš tako. Nisam baš stručnjak (ali Stručnjak) za Win pa zato i kažem subjektivni osećaj. Naime, čim pristigne veći broj update-ova za Win, i ne instaliraju se nekoliko nedelja, server počne da se “ponaša”.

Ja se stvarno izvinjavam svima na ovom maglovitom opisu, ali ne mogu detaljnije… jer nikad nije bilo isto i indikativno je bilo da nakon insalacije update-ova problema više nema…

A problemi su bili šarenoliki: usporenje odziva OS-a bez nekih razloga (event logovi OK, monitoring OK) ali se jednostavno uspori; ponekad se listeneri obaraju “sami od sebe” takođe, bez ikakvih vidljivih i razumljivih razloga iz logova; ponekad je pristup, onim ranije pomenutim, diskovima usporen do neupotrebljivosti; ponekad RMAN pravi backup relativno male baze i po 8-9 sati, ponekad…
Sve u svemu, evidentno je bilo da se problemi javljaju samo kada postoji veći broj neinstaliranih Win update-ova…

Dalje,

Sam proces instalacije Win update-a bi, ako je suditi po svim knjigama i uputsvima koje sam pročitao, trebao da teče na sledeći način: instaliraš na jednom nodu update, restart noda ako treba, pa to isto na svim sledećim nodovima… Aliii…
Instalacija ponekog update-a ne prolazi jer DB koristi neki sistemski dll u tom trenutku… Tako da… update nije prošao i vraćamo se na početak ovog pasusa…
Best practice bi bio ugasi RAC, instaliraj update, restartuj nodove ako treba ili digni RAC. Praktično, a?
Doduše, ovo nije slučaj čest, ali… bilo ih je par komada koji su me sludeli…

I da završim ovaj pasus, i ljudi iz Oracle-a i ljudi iz MSofta su dali preporuke da se Win update-uje do… nisam siguran kako se to u Win svetu zove, ali nazvaćemo ga Major Realize Number.
Npr. imaš Win 2008 R1… update radiš dokle god taj Win ne postaje R2 (Nekad se to zvalo, valjda, ServicePak)!!!
Ili gledano iz Oracle sveta, Win update radiš od drugog broja nadalje (11.1.X.Y gde se X i Y menjaju. Ni u kom slučaju ne radi update na 11.2).

My finall solution was: Turn off Update for Win. On every node. Full stop.

Glupo? Nepraktično? Netaktično?
Slažem se…
Otvoren sam za svaki predlog… da probamo!

Obzirom da ovde ima još što šta da se kaže, nastavak sutra.


Tips and Tricks: Brisanje duplih redaka iz tablice

Tuesday, 24.07.2012 – mradovan

Koliko puta vam se dogodilo da je u tablicu identičan redak insertan dva ili više puta, a tablica nije imala definirana ograničenja koja bi to spriječila, ili se eventualno identični redovi razlikuju samo po primarnom ključu. U svakom slučaju, od njih je potreban samo jedan, svi ostali su suvišni, i samo zauzimaju prostor, povećavaju vrijeme izvršenja upita i rade zbrku u programskom kodu i rezultatima upita.

Postoji više tehnika kako obrisati duplikate, neke od njih možete pogledati na Burleson Consulting linku

http://www.dba-oracle.com/t_delete_duplicate_table_rows.htm.

Moj favorit je kratki kod:

DELETE FROM
table_name A
WHERE
a.rowid > ANY (
SELECT B.rowid
FROM
table_name B
WHERE A.col1 = B.col1

);

Naravno, where uvjet je mjesto gdje ćete odrediti identifikatore retka, tako da može biti sastavljen od jednog ili više uvjeta vezanih AND i OR veznicima.


11gR2/Aix – Dedicated Server Proceses Have Large Usla Heap Segment Compared To Older Versions

Friday, 20.07.2012 – Dejan

Kojeg li neugodnog iznenađenja nakon upgradea sa 9i/10g na 11gR2 !? Opet Oracle bug – ne tako kritičan, ali i ovaj zadaje glavobolju!

Kako/Zašto?

Jedan informacioni sistem, koji je radio na Oracle verziji 10g, upgradean je na 11gR2.

Naime, nakon upgradea, odmah je primijećeno veće zauzeće RAM memorije. Pošto je znalo biti i oko 1000 spojenih korisnika istovremeno, dolazilo je do znatnih usporenja i pada performansi. Zajedno sa network i system administratorima sam morao naći uzrok ove pojave – drž, ne daj, kopaj, traži uzrok, usporedjuj sa sistemskim izvještajima od ranije – sve dok se ne sjetih ovog buga, na kojeg sam davno naletio tražeći informacije o relinkanju Oracle binariesa. Tad sam samo letimično preletio preko njega, ne čitajući detaljno, a čak sam i stavio ovaj MOS Note ID (“11gR2/Aix – Dedicated Server Proceses Have Large Usla Heap Segment Compared To Older Versions [ID 1260095.1]“) u Favorites, planirajući ga kasnije detaljno proučiti, no zaboravih na to. Ipak, u podsvijesti mi je ostala natuknica na sta se ovaj “bug” odnosio, pa sam ga brže-bolje “prostudirao”. 🙂

Naime, o čemu se radi !?

Radi se o bugu “PROCESSES CONSUMING ADDITIONAL MEMORY DUE TO ‘USLA HEAP‘”.

Od verzije 11gR2, Oracle server proces  (oracle binary) je linkan sa dodatnim link opcijama: –bexpful i -brtllib

Opcije -bexpfull i -brtllib su dodane, kako bi se omogućila “hot patching” funkcionalnost. U prethodnim verzijama 9i, 10g i 11gR1 ove opcije nisu korištene.

Rješenje?

IBM i Oracle preporučuju upgrade AIX OS-a na verziju 6.1 TL07 ili 7.1 TL01, te potom instalaciju Oracle Patcha Patch:13443029.
Patch 10190759 je za verzije starije od AIX 6.1 TL07 i 7.1 TL-01. U našem slučaju se radi o AIX 6.1 TL06.

Kako izgleda u praksi?

Kao što je opisano u MOS Note ID 1260095.1, zauzeće “USLA heap” je otprilike ovoliko:

Oracle Release -> (work USLA heap times 4k pages size)
11.2.0.1.0 -> 7M bytes
11.1.0.7.0 -> 60KB
10.2.0.4.0 -> 420KB

Ovako možete provjeriti, koliko “USLA heap” memorije jedan oracle proces stvarno zauzima:

– Oracle 10.2.0.4:

oracle$ ps -ef|grep oracleISPRO1

oracle 63701024 1 0 07:34:56 – 0:00 oracleISPRO1 (LOCAL=NO)

oracle$ echo `expr $(svmon -P 63701024 | grep “work USLA heap” | awk ‘{print $7}’) \* 4` kBytes
436 kBytes

– Oracle 11.1.0.7:

oracle$ ps -ef|grep oracleRSKPROD

oracle 39649488 1 0 Jun 19 – 15:50 oracleRSKPROD (LOCAL=NO)

oracle$ echo `expr $(svmon -P 39649488 |grep “work USLA heap” | awk ‘{print $7}’) \* 4` kBytes
76 kBytes

– Oracle 11.2.0.1:

oracle$ ps -ef|grep oracleCALCDB

oracle 57475154 1 0 08:23:11 – 0:02 oracleCALCDB (LOCAL=NO)

oracle$ echo `expr $(svmon -P 57475154 | grep “work USLA heap” | awk ‘{print $7}’) \* 4` kBytes
7220 kBytes

– Oracle 11.2.0.3 i 11.2.0.3.2 bez instaliranog patcha:

oracle$ ps -ef|grep oracleRMCPROD

ora11g 44105964 1 0 11:24:30 – 0:00 oracleRMCPROD (LOCAL=NO)

oracle$ echo `expr $(svmon -P 44105964 | grep “work USLA heap” | awk ‘{print $7}’) \* 4` kBytes
7484 kBytes

– Oracle 11.2.0.3 sa instaliranim patchom (10190759 za verzije starije od AIX 6.1 TL07 i 7.1 TL-01;  13443029 za verzije od AIX 6.1 TL07 i 7.1 TL-01 pa nadalje):

oracle$ opatch lsinventory

Installed Top-level Products (1):

Oracle Database 11g 11.2.0.3.0
There are 1 products installed in this Oracle Home.

Interim patches (1) :

Patch 10190759 : applied on Wed Jun 13 13:28:21 CEST 2012
Unique Patch ID: 14249255
Created on 8 Dec 2011, 15:26:38 hrs UTC
Bugs fixed:
10190759

oracle$ ps -ef|grep oracleRMCPROD

oracle 36569230 1 0 14:01:09 – 0:25 oracleRMCPROD (LOCAL=NO)

oracle$ echo `expr $(svmon -P 36569230| grep “work USLA heap” | awk ‘{print $7}’) \* 4` kBytes
80 kBytes

Mi smo morali odlučiti kako i šta dalje – da li se vratiti na 10g, isprobati onaj workaround opisan na My Oracle Support websajtu, instalirati ovaj interim patch ili preći na 11.2.0.3 sa najnovijim patchsetom. Testirali smo sa 11.2.0.3, ali bug postoji i u toj verziji, iako Oracle Support tvrdi da je bug rješen već u verziji 11.2.0.2.99, tako da nam to ne donosi ništa. Stoga je odluka pala na instaliranje patcha.

Edit 23.07.2012.:
Otvorio sam Service Request kod Oracle Supporta i pitao ih u vezi ovog problema – njihov odgovor je:

“Hi Dejan,

The issue you reported was investigated in:
Bug 13443029 – AIX: Excess “work USLA heap” process memory use in 11.2 on AIX (Doc ID 13443029.8)
fixed in 11.2.0.4.

Please apply Patch 13443029 – MEMORY LEAK FIX FOR ONLINE PATCHING / HOT PATCHING FOR RDBMS 11.2.0.3 AIX PPC64 on top of 11.2.0.3. This patch can be safely applied on top of 11.2.0.3.2 (your release) too. It is available for download at:

https://updates.oracle.com/download/13443029.html

Please read carefully the README file before applying the patch.

For reference please also read:
11gR2/Aix – Dedicated Server Proceses Have Large Usla Heap Segment Compared To Older Versions (Doc ID 1260095.1)
Things to Consider Before Upgrading to 11.2.0.3 to Avoid Poor Performance or Wrong Results (Doc ID 1392633.1) “


ALERT: Bug 8943287 ORA-1578 corrupt block with SQL*Net AUTH strings [ID 976852.1]

Thursday, 19.07.2012 – Dejan

U zadnje vrijeme sam naletio na nekoliko zeznutih Oracle bugova, koji jednom Oracle DBA mogu zagorčati život, a jedan od tih je i “Bug 8943287 ORA-1578 corrupt block with SQL*Net AUTH strings“. Detaljnije informacije o ovom problemu možete pročitati na stranici My Oracle Support [Note ID 976852.1].

Ukratko, u SQL*Net komponenti postoji bug, koji dolazi do izražaja, kada u serverskoj datoteci sqlnet.ora imate parameter sqlnet.inbound_connect_timeout podešen na vrijednost veću od 0. Mnogi Oracle DBA ne kreiraju sqlnet.ora na serveru, zbog čega važe standardne vrijednosti za sve parametre. Pošto je standardna (default) vrijednost za ovaj parameter 60, onda bi ga trebalo podesiti na 0.


Provjera Oracle db linka

Thursday, 19.07.2012 – mradovan

Baza podataka često ima potrebu komunicirati sa drugim bazama podataka, poželjno je da su to također Oracle baze podataka kojima pristupamo direktno, ali mogu biti i bilo koje druge, na koje se spajamo Heterogeneous Services tj. Database Gateway tehnologijama kao što su DG4ODBC, DG4MSQL isl.

U svakom slučaju, nakon instalacije konektora na kojeg ćemo spojiti Oracle bazu podataka, potrebno je deklarirati Connection string-ove koji definiraju poslužitelja i servis baze podataka, najčešće putem aliasa u TNSNAMES.ORA datoteci. Zatim kreiramo database link, uz korištenje aliasa ili punog connection stringa, i mogućnost pristupa udaljenoj bazi je ostvarena.

Naravno, uvijek postoji mogućnost da udaljena baza u određenom momentu postane nedostupna, iz raznih razloga, npr: server udaljene baze podataka je spušten ili se srušio, izmijenjene postavke konekcije (npr. IP adresa ili naziv servisa), prekinuta fizička komunikacija (greška na komunikacijskoj opremi ili je teta dok je usisavala slučajno iskopčala strujni ili UTP kabel) isl.

Uglavnom, u datom momentu udaljena baza nam postane nedostupna, a baš u tom trenutku se okinuo job koji pokreće pakiranu proceduru za sinkronizaciju podataka u tabllicama. I onda dolazi do havarije koju ćemo primijetiti tek naknadno: db link ne prolazi, i dolazi do invalidiranja paketa i drugih dijelova koda koje zatim treba rekompajlirati.

Da bi se to izbjeglo, napisao sam funkciju CHECK_DBLINK(name_in), koja za ulazni parametar ima ime db linka koji želimo testirati, a kao rezultat vraća broj: 1 za uspješno testiranje konekcije, -2019 ako je definicija dblinka pogrešna ili db link ne postoji te -12514 ako je udaljena baza nedostupna (radi se o ORA kodovima grešaka, pa ih nisam htio mijenjati, iako se rezultat funkcije može proizvoljno promijeniti).

Ispravnost db linka se naravno iz komandnog prompta može testirati naredbom TNSPING alias.

Funkcija CHECK_DBLINK:

CREATE OR REPLACE FUNCTION ARCHDB.check_dblink(name_in varchar2) RETURN NUMBER
AS
/********************************************************************
Opis: Provjerava raspoloživost i dostupnost udaljene baze putem db linka,
čije ime je ulazni parametar.
Ako je udaljena baza raspoloživa, funkcija vraća 1, u suprotnom vraća kod iznimke:
-2019   => alias udaljene baze nije definiran, tj. proziva se nepostojeci alias
-20514 => baza nije dostupna: dblink je invalidan, baza je spuštena ili je 
                   neka druga greška u komunikaciji
Autor: Mihael Radovan
Datum izrade: 18.07.2012
Zadnja izmjena: 18.07.2012
Verzija: 1.001
*********************************************************************/
BEGIN
execute immediate 'SELECT * FROM dual@' || name_in;

RETURN 1;

EXCEPTION
WHEN others THEN RETURN SQLCODE;
END check_dblink;
/

Ko je uneo ili promenio podatke – misterija kolona CreatedUser, CreatedDate, UpdatedUser, UpdatedDate

Friday, 29.06.2012 – Zidar

Uvod

Podaci koje cuvamo u bazama podataka su zivi, menjaju se konstantno. Ponekad je sasvim u redu cuvati samo tekuce podatke, i promene nas ne interesuju. Mnogo cesce, medjutim, promene nas interesuju. Sta je sada, a sta je bilo pre. To nije jednostavno pratiti u relacionim bazama podataka. Nije jednostavno, ali svima treba i svi se nekako dovijaju.

Prvi pokusaj da se prate promene dolazi iz zelje da znamo ko je i kada uneo podatke i da li su podaci od tada promenjeni , ko ih je i kada promenio. To je minimum koji zelimo da pratimo. I njaveci pocetnici brzo nauce da dodaju u tabelu kolone

CreatedDate – kada je red dodat u tabelu,

CreatedUser – ko je dodao red u tabelu (‘kreirao rekord’),

UpdatedDAte – kada je podatak promenjen,

UpdatedUser – ko je promenio podatak

Najcesce se sav napor tu I zavrsava – dodamo kolone I uzdamo se u Boga da se kolone nekako popune. Pa kad zatreba, necemo znati sta je bilo ranije u tabeli, ali cemo barem moci da pitamo onoga ko je napravio promene sta je uradio i zasto.

U ovom clanku, cemu ovu elementarnu stvar podici na malo visi nivo, tako da bar izgledamo kao profesionalci. Kod je proveren na MS SQL 2005. Uz male izmene, sve bi radilo I na MS SQL 2000.

CreatedUser,CreatedDate,UpdatedUser, UpdatedDate

Neka je data baza sa dve tabele, MyMasterTable i NyLookupTable. Evo skripte za kreiranje tabela:

IF Object_ID(‘MyLookupTable’) IS NOT NULL DROP TABLE MyLookupTable

GO

CREATE TABLE MyLookupTable

(Code int NOT NULL PRIMARY
, MyDescription varchar(15) NOT NULL
,
CONSTRAINT unqMyLookupTable_MyDescription UNIQUE(MyDescription)
);

INSERT INTO MyLookupTable VALUES
(1,‘tra, la, la’);

INSERT INTO MyLookupTable VALUES
(2,‘bla bla, bla’);

INSERT INTO MyLookupTable VALUES
(3,‘trla baba lan’);

;

IF Object_ID(‘MyMasterTable’) IS NOT NULL DROP TABLE MyMasterTable

GO

CREATE TABLE MyMasterTable(

MyPK int NOT NULL PRIMARY KEY,
ColA int NULL
,
ColB varchar(15) NOT NULL,

CONSTRAINT
fkMyMasterTable_ColA_MyLookupTAble

FOREIGN
KEY (ColA) REFERENCES MyLookupTAble(Code)

)

GO

Dve tabele, primarni kljucevi, jedan strani kljuc i NULL/NOT NULL ogranicenja. Zbog ovoga NULL/NOT NULL, vec smo iznad proseka. 🙂
Znaci, pored podataka koji se cuvaju u tabeli, zelimo da pratimo ko je i kada uneo podatke, i ko je i kada promenio podatke. Ovo nam verovatno korisnik nece navesti u specifikaciji, ali mi smo profesionalci i znamo da je korisno znati ko sta radi i kada po tabeli, pa ovo radimo sebe radi.

Da bi znali ko je i kada uneo podatke, dodacemo dve kolone

– CreatedDate datetime – kada je red dodat u tabelu

– CreatedUser datetime – ko je dodao red u tabelu

Primetite da ne dozvoljavamo NULL vrednosti ni u jednoj od kolona. Obe kolone imaju DEFAULT vrednosti, zbog cega ne moramo da ih navodimo u listi INSERT komande.

Ovako bi izgledalo kreiranje tabele:

IF Object_ID(‘MyMasterTable’) IS NOT NULL DROP TABLE MyMasterTable

GO

CREATE TABLE MyMasterTable

(

— stvarni podaci:

MyPK int NOT NULL PRIMARY KEY

, ColA int NULL

, ColB varchar(15) NOT NULL

— ko je i kada uneo podatke:

, CreatedDate datetime NOT NULL DEFAULT (GetDate())

, CreatedUser nchar(30) NOT NULL DEFAULT (system_user)

— veza sa lookup tabelom

, CONSTRAINT fkMyMasterTable_ColA_MyLookupTAble

FOREIGN KEY (ColA) REFERENCES MyLookupTAble(Code)

)

;

Ako pretpostavimo da niko nece menjati CreatedDate ili CreatedUser za sada smo OK Znacemo uvek ko je i kada uneo podatke u tabelu. To je lepo, ali mi hocemo da znamo i ko je i kada promenio podatke u tabeli? Ako nista drugo, uvek mozemo da pitamo coveka zasto je to uradio i ste je bilo u tabeli pre promene.

Zasto je pretpostavka da niko, namerno ili ne namerno, nece menjati podatke u kolonama za pracenje promena sumnjiva i opasna? Sumnjiva zato sto nemate garanciju da se to nece desiti. Opana zato sto cete imati pogresne podatke, a ovi podaci vam trebaju samo kada nesto inace krene lose.

Dodajmo kolone za pracenje promena u unetim podacima.

IF Object_ID(‘MyMasterTable’) IS NOT NULL DROP TABLE MyMasterTable

GO

CREATE TABLE MyMasterTable

(

— stvarni podaci

MyPK int NOT NULL PRIMARY KEY

, ColA int NULL

, ColB varchar(15) NOT NULL

— ko je i kada uneo podatke:

, CreatedDate datetime NOT NULL DEFAULT (GetDate())

, CreatedUser nchar(30) NOT NULL DEFAULT (system_user)

— Pracenje promena

, UpdatedDAte datetime NULL

, UpdatedUser nchar(30) NULL

— veza sa lookup tabelom

, CONSTRAINT fkMyMasterTable_ColA_MyLookupTAble

FOREIGN KEY (ColA) REFERENCES MyLookupTAble(Code)

)

;

Moramo da dozvolimo NULL vrednosti za UpdatedDAte i UpdatedUser, jer te kolone treba da dobiju vrednost tekkad dodje do promene. Kako cemo da popunimo Updated.. kolone? Automatski, pomocu trigera.

Minimalni triger izgleda ovako:

CREATE TRIGGER trgMyMasterTable_UPD ON
MyMasterTable

FOR UPDATE

AS

UPDATE MyMasterTable

SET

UpdatedDAte=getdate()

, UpdatedUser = system_user

FROM inserted AS I

JOIN MyMasterTable as
M ON I.MyPK = M.MyPk

;

Da vidimo da li sve ovo radi.

INSERT INTO MyMasterTable (MyPK,ColA,ColB) VALUES (1, 1, ‘Dobar!’);

INSERT INTO MyMasterTable (MyPK,ColA,ColB) VALUES (2, 2, ‘Bolji!’);

INSERT INTO MyMasterTable (MyPK,ColA,ColB) VALUES (3, 1, ‘Naj Bolji’);

Naravno da radi, vidi se ko je uneo podatke i kada:

SELECT * FROM MyMasterTable;


MyPK



ColA



ColB



CreatedDate



CreatedUser



UpdatedDAte



UpdatedUser



1



1



Dobar!



20120629 13:33:53.400



Test\Zidar



NULL



NULL



2



2



Bolji!



20120629 13:34:02.693



Test\Zidar



NULL



NULL



3



1



Naj Bolji



20120629 13:34:02.703



Test\Zidar



NULL



NULL


U redu sa MyPK = 3 rec ‘najbolji’ je pogresno otkucana.
Pokusajmo da ispravimo gresku.

UPDATE
MyMasterTable

SET ColB = ‘Najbolji’

WHERE MyPK = 3

Sta sada imamo u tabeli:

SELECT * FROM MyMasterTable;


MyPK



ColA



ColB



CreatedDate



CreatedUser



UpdatedDAte



UpdatedUser



1



1



Dobar!



20120629 13:33:53.400



Test\Zidar



NULL



NULL



2



2



Bolji!



20120629 13:34:02.693



Test\Zidar



NULL



NULL



3



1



Najbolji



20120629 13:34:02.703



Test\Zidar



20120629 13:36:44.210



Test\Zidar


Za sada, sve izgledad dobro. Medjutim, uvek ima mesta za poboljsanja. Evo nekoliko poboljsanja koja se u praksi cesto zaboravljaju:

Prvo, ne zaboravimo da se izmena desava uvek posle unosenja podataka, pa treba da bude UpdateDAte>CreateDate.

Drugo, kolone UpdateDate i Updateduser moraju ili obe da budu NULL ili obe da budu NOT NULL. Podaci ne smeju d apostoje u jednoj a nedostaju u drugoj koloni.

Trece, svakao ne zelimo da bilo ko naknadno menja neku od kolona koje prate promene.

Prva dva poboljsanja se lako resavaju upotrebom CHECK constraints.

— Datum izmena mora doci kasnije od unosa

ALTER TABLE MyMasterTable WITH
CHECK

ADD CONSTRAINT ckMyMasterTable_UpdPosleCreate

CHECK (CreatedDate <
UpdatedDAte)

;

Primetite da nismo stavili (CreatedDate < UpdatedDAte OR UPdatedDate IS NULL). Nema potreba, jer kad izraz je u CHECK constraint jendk NULL smatra se da je pravilo zadovoljeno (nije prekrseno)

Da li ogranicenje radi:

UPDATE
MyMasterTable

SET UpdatedDAte = ‘19600310’ — 10 Mart 1960, veoma star datum 🙂

WHERE MyPK = 3

Ne ide, dobijemo ovu poruku:

Msg 547, Level 16, State 0, Line 1

The UPDATE statement conflicted with
the CHECK constraint
“ckMyMasterTable_UpdPosleCreate”. The
conflict occurred in database
“Dejan”, table
“dbo.MyMasterTable”.

The statement has been
terminated.

Za drugo poboljsanje, “kolone UpdateDate i Updateduser moraju ili obe da budu
NULL ili obe da budu NOT NULL”
, ogranicenje moze da se napise na vise nacina. Ja cu da
uradim ovako ovako:

Mora da bude zadovoljeno:

Ako (UpdatedDAte IS NULL) onda (UpdatedUser IS NULL)

i

Ako (UpdatedDAte IS NOT NULL) onda (UpdatedUser IS NOT NULL)

Ovo su dva izraza tipa A=>B. A => B ne moze da se
predstavi u Transact SQL. Medjutim, A=>B moze da se napise kao ((NOT A) OR
B), sto se lako pretvara u CHECK ogranicenje.

ALTER TABLE MyMasterTable WITH
CHECK

ADD CONSTRAINT
ckMyMasterTable_UpdatedDAteUpdatedUser_vs_NULL

CHECK (

(NOT (UpdatedDAte IS NULL) OR UpdatedUser IS NULL)

AND

(NOT (UpdatedDAte IS NOT NULL) OR (UpdatedUser IS NOT NULL))

)

;

Oslobadjanjem od zagrada izraz u CHECK ogranicenju bi mogao
da se naoko uprosti, ali bi bio znatno tezi za citanje i razumevanje.

Da li poslednje ogranicenje radi:

UPDATE
MyMasterTable

SET
UpdatedDAte = NULL

WHERE MyPK = 3

Msg 547, Level 16, State 0, Line 1

The UPDATE statement conflicted with
the CHECK constraint
“ckMyMasterTable_UpdatedDAteUpdatedUser_vs_NULL”. The conflict occurred in
database “Dejan”, table
“dbo.MyMasterTable”.

The statement has been
terminated.

Primetite da se CHECK constraint se aktivirala pre trigera i
zaustavila tarnsakciju.

Testiranje nije gotovo:

UPDATE
MyMasterTable

SET
UpdatedUser = NULL

WHERE MyPK = 3

Msg 547, Level 16, State 0, Line 1

The UPDATE statement conflicted with
the CHECK constraint
“ckMyMasterTable_UpdatedDAteUpdatedUser_vs_NULL”. The conflict occurred in
database “Dejan”, table
“dbo.MyMasterTable”.

The statement has been
terminated.

Sta bi bilo kad bismo uradili ovo:

UPDATE
MyMasterTable

SET

UpdatedUser = NULL

, UpdatedDAte = NULL

WHERE MyPK = 3

;

Sada nas je CHECK pustio da prodjemo, ali je triger odradio
svoj deo posla – zabelezio je ko je i kada promenio podatke. Ovom smo dobili
laznu promenu – promenili smo vreme polsenje promene, a da se u stvari u tabeli nije nista promenilo.

U svakom slucaju, CHECK constraint i trigger, u sadejstvu, nisu dozvolili da anuliramo podatke.

SELECT * FROM MyMasterTable;


MyPK



ColA



ColB



CreatedDate



CreatedUser



UpdatedDAte



UpdatedUser



1



1



Dobar!



20120629 15:02:16.940



Test\Zidar



NULL



NULL



2



2



Bolji!



20120629 15:02:27.820



Test\Zidar



NULL



NULL



3



1



Najbolji



20120629 15:02:27.830



Test\Zidar



20120629 15:05:06.337



Test\Zidar


Sta se desava ako pkusamo da uesemo UpdateUser i CreateUser
po zelji:

UPDATE
MyMasterTable

SET

UpdatedUser = ‘Yoggy Bear’

, UpdatedDAte = ‘2012-06-29 15:22:27.830’ — 20 minuta posle CreatedDate

WHERE MyPK = 3

;

SELECT * FROM MyMasterTable;


MyPK



ColA



ColB



CreatedDate



CreatedUser



UpdatedDAte



UpdatedUser



1



1



Dobar!



20120629 15:02:16.940



Test\Zidar



NULL



NULL



2



2



Bolji!



20120629 15:02:27.820



Test\Zidar



NULL



NULL



3



1



Najbolji



20120629 15:02:27.830



Test\Zidar



20120629 15:07:26.530



Test\Zidar


Triger je odradio svoj posao i nije uneti vreme koje smo mi zadali, nego vreme kad je proemna zaista upisana u atbelu. Podaci ipak nece biti vise tacni, jer smo izgubili stvarni datum promene. Ovo cemo da resimo kad budemo resavali poslednje od tri poboljsanja koje smo naveli: svakao ne zelimo da bilo ko naknadno menja neku od kolona koje prate promene.

Videli smo da se UpdateDate i UpdatedUser tesko mogu promeniti na nesto sto mi hocemo – trigger ce uvek upisati trenutak promene i ko je pokusao promenu, pa makar
menjali kolone UpdateDate i UpdatedUser.

Mozemo li da promenimo CraetedUser ili CreatedDate? Mozemo, ukoliko postujemo CHECK constraints.

UPDATE
MyMasterTable

SET

CreatedUser = ‘Piksi’ — korisnik uopste
ne postoji u sistemu

, CreatedDAte = ‘19020101’ — 100 godina ranije od originalnog CreatedDate

WHERE MyPK = 1

;

SELECT * FROM MyMasterTable;


MyPK



ColA



ColB



CreatedDate



CreatedUser



UpdatedDate



UpdatedUser



1



1



Dobar!



19020101 00:00:00.000



Piksi



20120629 15:19:03.993



Test\Zidar



2



2



Bolji!



20120629 15:02:27.820



Test\Zidar



NULL



NULL



3



1



Najbolji



20120629 15:02:27.830



Test\Zidar



20120629 15:11:32.287



Test\Zidar


Kako da sprecimo promenu CreatedDate, CreatedUser i UpdatedDAte,UpdatedUser?

Mozemo u trigeru da utvrdimo da li korisnik pokusava da promeni neku od ove cetiri kolone i da to sprecimo. U MS SQL, funkcija koja nam pomaze u ovom slucaju je UPDATE(<ime kolone>)

ALTER TRIGGER trgMyMasterTable_UPD ON
MyMasterTable

FOR UPDATE

AS

— Novo:

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

— Staro:

UPDATE MyMasterTable

SET

UpdatedDAte=getdate()

, UpdatedUser = system_user

FROM inserted AS I

JOIN MyMasterTable as
M ON I.MyPK = M.MyPk

;

GO

Triger je podugacak, ali vidite da su dodatne komande jednostavne. Ako se manja bilo koja od nasih kolona za pracenje proemna, obustavlja se cela tarnsakcija (rollback) i izlazi se iz trigera (return). Da
testiramo:

UPDATE
MyMasterTable

SET

CreatedUser = ‘Batman’ — korisnik uopste
ne postoji u sistemu

, CreatedDAte = ‘1906-06-29 13:33:53.400’ — 100 godina ranije od originalnog
CreatedDate

, UpdatedUser = ‘SpidreMan’ — korisnik uopste ne postoji u sistemu

, UpdatedDAte = ‘1959-06-29 13:33:53.400’ — 100 godina ranije od originalnog
CreatedDate

WHERE MyPK = 2

;

Msg 3609, Level 16, State 1, Line 1

The transaction ended in the trigger. The
batch has been aborted.

SELECT * FROM MyMasterTable

;

Znaci, imamo cetiri kolone, dve Created.. i dve Updated…
Created.. kolone se popunjavaju preko DEFAULT vrednosti i ne smeju biti NULL.

UPpadted.. kolone se popunjavaju kroz triger, u momentu izmene. CHECK constraints sprecavaju nelogican unos podataka.

Triger ne dozvoljava da se menjaju kolone koje prate ko je i kada uneo ili izmenio podatke.

Moguca je jos jedna sitna popravka. I dalje mozemo da belezimo lazne promene.

Lazna promena je kad uradimo UPDATE, i damo novu vrednost koja je jednaka staroj vrednosti.

SELECT *
FROM
MyMasterTable
WHERE
MyPK=1;


MyPK



ColA



ColB



CreatedDate



CreatedUser



UpdatedDate



UpdatedUser



1



1



Dobar!



19020101 00:00:00.000



Piksi



20120629 15:19:03.993



Test\Zidar


Za MyPK = 1, colB = ‘Dobar!’.

Pokusajmo ovo:

UPDATE MyMasterTable

SET ColB = ColB — ista vrednost ce se upisati preko postojece

WHERE MyPK=1

;

SELECT *
FROM
MyMasterTable
WHERE
MyPK=1;


MyPK



ColA



ColB



CreatedDate



CreatedUser



UpdatedDate



UpdatedUser



1



1



Dobar!



19020101 00:00:00.000



Piksi



20120629 15:40:14.890



Test\Zidar


Podaci se nisu promenili, ali se datum u UpdatedDate promenio! To nije dobro.

Lazne promene otkrivamo u trigeru, tako sto uporedimo tabelu INSERTED sa nasom tabelom. Nacin na koji poredimo atbele inserted I nasu originalnu tabelu radi u MS SQL 2005. Verzija 2000 ne koristi EXCEPT, ali se moze koristiti LEFT JOIN.

ALTER TRIGGER trgMyMasterTable_UPD ON
MyMasterTable

FOR UPDATE

AS

— Najnovije: da li se Inesrted razlikuje od
MyMasterTable?

— Ako je Inserted = MyMasterTable onda EXCEPT vraca
prazan skup <=> NOT EXISTS

IF NOT EXISTS

(

SELECT I.MyPk, I.ColA, I.ColB

FROM Inserted AS I

EXCEPT

SELECT M.MyPk, M.ColA, M.ColB

FROM
MyMasterTable AS M

JOIN Inserted AS I ON I.MyPk = M.MyPk

)

BEGIN

rollback

return

END

— Novo:

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

IF UPDATE(CreatedDate)

BEGIN

rollback

return

END

— Staro:

UPDATE
MyMasterTable

SET

UpdatedDAte=getdate()

, UpdatedUser = system_user

FROM inserted AS I

JOIN
MyMasterTable as M ON
I.MyPK = M.MyPk

return

;

GO

SELECT *

FROM
MyMasterTable

WHERE MyPK=1

;

MyPK ColA ColB CreatedDate CreatedUser UpdatedDAte UpdatedUser

1 1 Dobar! 19020101 00:00:00.000 Piksi 20120629 15:40:14.890 Test\Zidar

UPDATE MyMasterTable

SET ColB = ColB — ista vrednost ce se upisati preko postojece

WHERE MyPK=1

;

Msg 3609, Level 16, State 1, Line 1

The transaction ended in the trigger. The batch has been aborted.

SELECT *
FROM
MyMasterTable
WHERE
MyPK=1;


Zakljucak

Pokazali smo kako se moze obezbediti elementarno i minimalno pracenje ko je i kada uneo ili promenio podatke.

Nista narocito, reci ce mnogi, svako dete zna da doda kolone Created… i Updated…. Istina je, ali smo ovde sve podigli na malo ozbiljniji nivo, time sto smo sprecili nelogicne podatke i upis laznih promena.

A ako pogledamo pazljivo CHECK ogranicenja i triger koji smo morali napraviti, i nije sve bas tako jednostavno. 🙂

Za ozbiljan rad, ovo sto smo dobili nije dovoljno. Sta vredi znati ko je promenio podatke, ako ne znamo sta smo imali pre promene. Naravno da ako znamo ko je napravio proemnu, uvek mozemo da pitamo tu osobu zasto je to uradjeno I sta je bilo pre promene, ali to nije bas najprofesionalnije ponasanje, zar ne.

Sledeci clanak ce pokazati kako mozemo da cuvamo istoriju – stari podaci ne nestaju nego se cuvaju u zasebnoj tabeli. Trebace nam trigeri, a trebace i znati kako se istorijski podaci citaju. Pokazacemo klasican nacin, poznat preko 30 godina, a onda mozda pokazati i neka modrnija resenja.

Pokazana resenja ne treba shvatiti kao recept za rad. Cilj je da pocnemo razmisljati o stvarima koje lako zaboravimo – sacuvati podatke, ali i spreciti mugucu zloupotrebu ili kontaminaciju podataka, na visem nivou od FOREIGN KEY.


Oracle Apex 4.2

Tuesday, 26.06.2012 – mradovan

Svjetlo dana je ugledao Oracle Application Express 4.2 Early Adopter. Svi zainteresirani za testiranje i isprobavanje novih mogućnosti alata mogu zatražiti svoj workspace i igrati se “u oblaku” na https://apexea.oracle.com.

Opise novih mogućnosti možete vidjeti na http://apex.oracle.com/pls/apex/f?p=38997:1.


UPOZORENJE za sve E-Business Suite korisnike: Disable JRE Auto-Update to Avoid Upgrading to JRE Version 7

Tuesday, 19.06.2012 – Dejan

Vidio sam ovo upozorenje prije nekoliko dana, ali iako lično nisam zadužen za E-Business Suite kod nas u firmi, smatram ga veoma važnim, tako da sam se zaprepastio kad sam uvidio da radne kolege zadužene za E-Business, nisu uopšte bili informisani u vezi ovog problema.

Ukratko, mnoge E-Business Suite aplikacije ne rade sa Java verzijom 7 i ukoliko je uključen automatski Java update, onda dolazi do problema u radu dotične aplikacije.

Detaljnije o ovom problemu možete pročitati na My Oracle Support websajtu (“ALERT: Disable JRE Auto-Update to Avoid Upgrading to JRE Version 7” [ID 1467815.1) kao i na ovom linku “URGENT BULLETIN: All E-Business Suite End-Users Must Manually Apply JRE 6 Updates“.


TOAD: “You have no Oracle clients installed”

Tuesday, 19.06.2012 – Dejan

Dobih nekoliko poruka na email, gdje me ljudi mole za pomoć u vezi instalacije TOAD-a na Windows 64-bit mašini. Na svu sreću (ili nažalost), već sam imao iskustva s tim problemom.

Nakon što sam prije par godina instalirao TOAD na Windows 7 64-bit, dočekala me je poruka iz naslova – “You have no Oracle clients installed“.

Ma šta nema, ma**cu ti tvoju! Imam ih 3 čak, a ti ćeš meni da nemam !!

Džaba mi i reinstall Oracle clienta, opet ne fercera… Drž ne daj, progooglah pola interneta dok ne nađoh rješenje za taj problem! Naime, TOAD jadničak podržava samo 32-bit Oracle client, tako da sam morao skinuti Oracle client 32-bit i instalirati ga samo da bih mogao koristiti TOAD. Lejmerski 🙁

Inače, Quest je izbacio uputstvo kako riješiti taj problem: Getting “You have no Oracle clients installed” error message when connecting or launching / starting Toad for Oracle.


Slanje emaila iz Oracle 11g baze podataka

Monday, 18.06.2012 – mradovan

UTL_MAIL je paket predstavljen u 10g verziji baze podataka. Omogućava jednostavno slanje emaila iz Oracle baze podataka jednim PL/SQL izrazom. Paket nije standardno instaliran prilikom instalacije baze podataka, što znači da ga treba naknadno instalirati i podesiti. Koraci su sljedeći:

1. Logirati se kao sys sa sysdba ulogom i pokrenuti izvšavanje instalacijskih skripti:

  • – sqlplus / as sysdba
  • – @$oracle_home\rdbms\admin\utlmail.sql;
  • – @$oracle_home\rdbms\admin\utlmail.sql;

2. Definirati init.ora parametar, pri čemu xxx.xxx.xxx.xxx je IP adresa DB servera:

a) ako je instanca startana sa SPFILE-om:

  • alter system set smtp_out_server=’xxx.xxx.xxx.xxx’ scope=both;

b) ako je instanca startana sa PFILE-om:

  • – alter system set smtp_out_server=’xxx.xxx.xxx.xxx’ scope=both;
  • – dodati u initSID.ora redak: smtp_out_server = ‘xxx.xxx.xxx.xxx’

3. Dodijeliti privilegije pokretanja procedura iz UTL_MAIL paketa korisniku koji će slati emailove (nazvat ćemo ga moj_korisnik):

  • grant execute on utl_mail to moj_korisnik;

4. Oracle 11g zahtijeva i Access Control List (ACL):

DECLARE
BEGIN
   DBMS_NETWORK_ACL_ADMIN.CREATE
    (
       acl => ‘utlmailpkg.xml’,
       description => ‘Slanje emaila’,
       principal => ‘MOJ_KORISNIK’,
       is_grant => true,
       privilege = ‘connect’
   );

   DBMS_NETWORK_ACL_ADMIN.ADD_PRIVLEGE
    (
       acl => ‘utlmailpkg.xml’,
       principal = ‘MOJ_KORISNIK’,
       is_grant = true,
       privilege = ‘resolve’
    );
   
   DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL
    (
       acl => ‘utlmailpkg.xml’,
       host => ‘xxx.xxx.xxx.xxx’,
       lower_port = 25,
       upper_port = NULL
    );

COMMIT;

END;

Provjera ispravnosti parametra:

  • select * from v$parameter where name = ‘smtp_out_server’;

Provjera ACL liste:

  • select * from dba_network_acls;

Slanje emaila:

exec sys.utl_mail.send
(
sender => ‘sender@mydomain.com’,
recipients => ‘recipient@domain.com’,
subject => ‘Naslov poruke’,
message => ‘Ovo je tijelo poruke u kojem mogu pisati svašta’
);