Mérnök lettem

Ma megvolt a diploma védése, melynek során mérnökké nyilvánítottak. Juppi!

Diplomamunkám ezen a címen érhető el, a bizottság ötösre értékelte az elvégzett munkám, és így végeredményben a diplomám minősítése négyes lett. A védésen tartott prezentációm diái itt vannak.

Fordító

Rövid bejegyzés lesz, mostanában picit kevés a szabadidőm (diplomamunka, záróvizsgák):


A fenti linken pár screenshot látható a diplomamunka keretében implementált T-SQL - PL/SQL fordítóról. Amolyan proof-of-concept munka, ANTLR+StringTemplate segítségével. Ha leadtam a leadni valókat és befejeztem a befejezni valókat, majd írok egy összefoglalást, hogy mennyire fényjézus ez az ANTLR.

Tranzakciókezelés és zárolás SQL Server és Oracle alatt

Az SQL Server és az Oracle adatbáziskezelő tranzakció kezelése különbözik egymástól.

Arról már írtam egy korábbi bejegyzésben, hogy egy tranzakció SQL Server alatt (alapértelmezetten) explicit, azaz begin transaction … commit/rollback transaction utasítások jelölik az elejét illetve a végét, míg Oracle alatt az első végrehajtható SQL utasításkor kezdődik, és commit/rollback (vagy hiba, disconnect) hatására ér véget. Viszont nem ez az egyedüli különbség, a két rendszer a zárakat is eltérően kezeli.

Az SQL Server a SELECT utasítás hatására egy shared zárat helyez a táblára, míg az Oracle semmilyet se. Gondoljuk végig, hogy ez mit jelent: egy SELECT tehát csak akkor tud lefutni SQL Server alatt, ha rá tud tenni egy shared zárat, azaz a táblán maximum shared zár van jelenleg (nincs exclusive lock). Ennek következtében a SELECT és az UPDATE utasítások fizikailag szerializáltan hajtódnak végre.

Ezzel szemben Oracle alatt egy SELECT utasítást nem blokkolja semmi, és egy SELECT utasítás nem blokkol senkit (FOR UPDATE klauzula nélkül).

Képzeljünk el egy olyan T-SQL tárolt eljárást, ami kiolvassa egy számla egyenlegét egy változóba, elszöszmötöl egy darabig, majd – ha van elég pénz az egyenlegen – csökkenti azt. Ennek az eljárásnak a kódja valahogy így nézhet ki:

declare @balance money
begin transaction
select @balance = balance from tran_balance where userid=20
-- some very serious calculation..
if @balance > 1500
update tran_balance set balance=balance-1500 where userid=20
commit transaction

Mi itt a hiba? Ez a kód bizonyos időzítés mellett helyesen működik SQL Server alatt, Oracle alatt viszont (alapértelmezett izolációs szinten) szinte sehogy se.
Tegyük fel, hogy az alábbi ábrán látható módon indul el két tranzakció.
SQL Server alatt az alábbi lépések fognak történni:
  • A SELECT kirak egy shared lock-ot a táblára
  • Az UPDATE kirak egy exclusive lock-ot a táblára
  • Indulna a 2. tranzakció, de a SELECT nem tud shared lock-ot kirakni, mert a másik tranzakció exclusive lock-ja még érvényben van
  • Az első tranzakció lefut, végrehajtódik a COMMIT utasítás
  • Elindul a 2. tranzakcióban levő SELECT utasítás is, és az első tranzakció által véglegesített értéket olvassa ki.

Ha az időzítésen variálunk egy picit, pl: feljebb „toljuk” a 2. tranzakció SELECT-jét az első tranzakció SELECT és UPDATE utasítása közé, akkor SQL Server-en is „nem várt” működést tapasztalhatunk (akkor nem várt, ha nem tudjuk, hogy működik :)):
  • lefut az első SELECT
  • lefut a második tranzakció SELECT-je is, a shared lock miatt (a shared lock-ok egymással kompatibilisek)
  • lefut az első UPDATE
  • a második UPDATE blokkolódik, egészen addig, amíg az első tranzakció nem fejeződik be.

Mi a helyzet Oracle alatt? A SELECT utasítás mindkét (bármilyen) esetben le fog futni, kiolvasva az utolsó elcommitált értéket. A fenti példakód esetében tehát a konkurens tranzakciók lehet, hogy úgy terhelik be az egyenleget, hogy már nincs is rajta fedezet. Azaz, ha „ész nélkül” (automatikusan) fordítjuk át T-SQL kódjainkat PL/SQL-re, akkor a fenti „kicsit” rossz kódból nagyon rossz kódot kapunk.

Ebben a példában két lehetőség van a javításra, Oracle oldalon. Az egyik a SERIALIZABLE izolációs szint használata: ezen a szinten levő tranzakciók ha egy olyan táblát szeretnének módosítani, amit egy másik – még el nem commitált – tranzakció már módosított, akkor hibát kapunk:

Hiba a(z) 1. sorban:
ORA-08177: ehhez a tranzakcióhoz nem lehet sorbarendezni a hozzáférést
ORA-06512: a(z) helyen a(z) 6. sornál

A másik megoldás a LOCK TABLE használata: még mielőtt bármit is olvasnánk a táblából, rárakunk egy zárat. Ezzel elérhetjük az SQL Server-éhez hasonló működést, azzal a különbséggel, hogy nem a SELECT utasítás fog blokkolódni, hanem az előtte lévő LOCK TABLE.

HOUG 2009

Idén először vettem részt a Magyarországi Oracle Felhasználók Konferenciáján, melyet április 6-9. között a siófoki Hotel Azúrban tartottak.

Én szerda reggel érkeztem meg, kiváncsian vártam Kardkovács Zsolt és Éberhardt Péter előadását a Közel valós idejű, adaptív műsorajánló digitális televíziózáshoz címmel, amikor is a terem előtt hallottam, hogy sajnos elmarad. Helyettük Sárecz Lajos (akinek a jóvoltából vehettem részt a konferencián) tartott előadást Real Application Testing témában.

A szerdai nap további részét főleg az Üzleti intelligencia és az Adatbiztonság szekciók előadásain töltöttem, illetve befurakodtam Czinkóczki László - szokásosan - teltház előtt tartott SQL érdekességek, újdonságok előadására.

Az előadásokat egy számomra rendkívül érdekes panelbeszélgetés zárta szintén adatbiztonság témában, ahol a meghívott vendégek Kirner Attila (PSZÁF), Jakab Péter (MKB Zrt.), Dr. Suba Ferenc (Magyar Kormány Informatikai Biztonsági Incidenskezelő Központ), Antal Lajos (PwC Kft.), Bártfai Attila (kancellár.hu Zrt.), Keleti Arthur (KFKI Zrt.) voltak.

A beszélgetésről talán mindent elmond az, hogy az első kérdés megválaszolása, kivesézése 50 percig tartott.

Az esti vacsorát látványos zenés programok, sör, beszélgetések színesítették, majd egy csocsó-"bajnokság" :)

A csütörtöki nap számomra továbbra is BI + SQL/PLSQL témában telt, az adatbiztonság helyét Tóth Balázs kiváló Change Management a 11g Oracle adatbázisban előadása vette át.

Én hasznosnak éreztem a konferenciát, csak néha tényleg a bőség zavarával kűzködtem: túl sok jó előadás volt :)

Oracle 4 prezident :)

A Világgazdaság Online arról számol be, hogy felmerült Füzes Péter, az Oracle Hungary ügyvezető igazgatójának neve lehetséges gazdasági miniszterként.

Ennek alátámasztására azt írják, hogy a szakember együtt sportol Bajnai Gordon miniszterelnökkel. :)

PLS-00990 és PLS-00989

Ez a két hibaüzenet kissé elkeserített, ugyanakkor megmagyarázza számomra, hogy az SQL Developer Migration Workbench miért több kurzor változó segítségével oldja meg az eredményhalmazok visszaadását a tárolt eljárásokból, és miért nem egy kurzor változókat tartalmazó tömb segítségével.

A válasz egyszerű: mert nem lehet kurzor változókat tartalmazó tömböt készíteni:
PLS-00990: Index Tables of Cursor Variables are disallowed
PLS-00989: Cursor Variable in record, object, or collection is not supported by this release
A "this release" pedig az Oracle Database 11g Enterprise Edition Release 11.1.0.6.0. Pedig milyen jó ötletnek tűnt.. :)

Oracle Junior Professional Program

Tegnap éjszaka megérkezett a tájékoztató e-mail, miszerint:
Örömmel értesítünk, hogy az előző félévben tett sikeres vizsgád eredményéül feltöltheted adataidat abba az adatbázisba, amelyből partnereink kiválasztják azokat, akiknek munkaszerződést ajánlanak és így részt vehetnek az intenzív nyári képzésen.
Végülis, ahhoz képest, hogy legkésőbb január végére ígérték a tájékoztatást, nem is olyan rossz.. :)

Kicsit deja vu érzésem van, tavaly is voltam vizsgázni, tavaly is volt ilyen nyári program, tavaly is mehettem volna, de végül nem tettem, maradtam annál a cégnél, ahol már 2007 óta dolgozok, idén márciustól már teljes állásban.

Hogy idén mi lesz, még nem tudom, viszont mivel június 8. a jelentkezési határidő, ezért még bőven van időm eldönteni. Majd meglátjuk.

Formális nyelvek és az ANTLR

A nyelv formális definíciója

Jelölje ∑ a nyelv karakterkészletének véges halmazát. Ezt a halmazt nevezzük a nyelv alfabetájának.

Legyen ∑* a ∑ alfabetából alkotott véges, de nem korlátos hosszúságú jelsorozatok halmaza. Nem üres ∑ halmaz esetén ∑* halmaz megszámlálhatóan végtelen számosságú.

Egy adott alfabetából képezhető összes lehetséges jelsorozatba beletartozik az üres jelsorozat, az ε is.

Valamely - egy adott ∑ alfabeta felett értelmezett - L nyelv a ∑* halmaz egy tetszőleges részhalmaza.

Fordítók

Egy fordító általánosságban nem más, mint egy olyan program, ami valamilyen bemeneti jelsorozatra valamilyen kimeneti jelsorozattal válaszol. Bemeneti jelsorozat alatt nem akármilyen karaktereket, hanem a nyelv karakterkészletébe, alfabetájába tartozó karaktereket értünk. A bemeneti jelsorozatokat szokás még mondatoknak nevezni. Tehát a fordító egy L nyelvbe tartozó s mondatot valamilyen t mondatra képez le.

A nyelveket formálisan nyelvtanok segítségével írhatjuk le. Nyelvtanok segítségével a nyelv mondatai levezethetőek, generálhatóak. A gyakorlatban a nyelvtanokat mégis arra használjuk, hogy eldöntsük, egy mondat eleme-e az adott nyelvnek.

A nyelvtanokat egy négyes határozza meg:

G = (N, ∑, P, S)

ahol N a nyelvtani szimbólumok véges halmaza, ∑ a nyelv alfabetája, P a levezetési, vagy másnéven helyettesítési szabályok összessége, S pedig a mondatszimbólum.

Chomsky-féle nyelvosztályok

A nyelvtanokat meghatározó helyettesítési szabályok (vagy másnéven levezetési szabályok) bonyolultsága alapján Chomsky a nyelveket osztályokba sorolta.

3-as nyelvosztály

A 3-as nyelvosztály nyelvtanaiban csak kétféle levezetési szabálytípus engedélyezett:

A → a illetve A → aB

ahol a nemterminális szimbólumokat nagy, a terminális szimbólumokat kis betűvel jelöljük.

Az ilyen alakú nyelvtanokat reguláris (jobbreguláris) nyelvtanoknak nevezzük. Ezek a nyelvtanok generálják a reguláris nyelveket.

2-es nyelvosztály

A 2-es nyelvosztály helyettesítési szabályainak alakja:

A → α

ahol α tetszőleges terminális és nemterminális szimbólumokat tartalmazható jelsorozat. Az ilyen alakú szabályokat úgy lehet értelmezni, hogy az A nemterminális szimbólum a környezetétől függetlenül bármikor helyettesíthető az α jelsorozattal. Ezért ezen nyelvosztály nyelveit környezetfüggetlen, Context Free (CF) nyelveknek nevezik.

1-es nyelvosztály

Az 1-es nyelvosztály helyettesítési szabályainak alakja:

βAγ → βαγ

azaz a A → α szabály csakis a β-γ környezetben alkalmazható. Ezért ezeket a nyelveket környezetfüggő, Context Sensitive (CS) nyelveknek nevezik.

0-ás nyelvosztály

A 0-ás nyelvosztályban alkalmazható szabályokra nézve nincsen korlátozás.

ANTLR nyelvtanok

Az ANTLR számára megadhatunk nyelvtanokat, amik nem mások, mint szabályok halmazai. Ezekből a szabályokból az ANTLR egy rekurzív-leszálló (recursive-descent) elemzőt készít, ami az adott nyelv mondatait ismeri fel, azaz eldönti, hogy az adott mondat eleme-e a nyelvnek, a mondat követi-e a nyelvnek a nyelvtanban leírt szabályait.

Például, az ANTLR jelölésrendszerét használva (ami nem más, mint az EBNF (Extended Backus-Naur Form) jelölésrendszer) egy C-szerű nyelv változódeklarációit leíró nyelvtan egy részlete így nézhet ki:

variableDef
: declaration SEMICOLON
| declaration EQUALS expr SEMICOLON
;

ahol a ':' előtt álló rész egy nemterminális szimbólum, az utáni rész pedig nemterminális (kis kezdőbetűvel kezdődő) és terminális (nagy kezdőbetűvel kezdődő) szimbólumok halmaza. Ez a levezetési szabály két alternatívát tartalmaz: egy sima változó deklaráció, vagy deklaráció inicializációval. Az alternatívákat a '|' szimbólum választja el.

Használva az előzőleg bevezetett formális jelölést, a fenti nyelvtan így néz ki:
  • N: variableDef, declaration, expr
  • ∑: SEMICOLON (';'), EQUALS ('='), illetve egyéb, a fenti szabályban fel nem sorolt karakterek
  • P: a szabályok halmaza
  • S: a mondatszimbólum, ahonnan indul a levezetés. ANTLR-ben bármelyik nemterminális szimbólum lehet mondatszimbólum.

Az EBNF jelölésrendszer CF nyelvtanok leírását teszi lehetővé. Sajnos több nyelv nem illeszkedik a CF nyelvosztályba, egy adott kifejezés szintaktikai értelmezéséhez szükséges a kontextus ismerete. Például, C++ nyelvben a T(i) kifejezés kétféleképpen is értelmezhető: lehet függvényhívás, vagy típuskonverzió is, attól függően, hogy T függvény-e, vagy egy típus.

Az ANTLR az ehhez hasonló problémákat úgynevezett predikátumok használatával oldja meg: szemantikus és szintaktikus predikátumokkal.

A szemantikus predikátumok szemantikus megszorításokat fejeznek ki. Három esetet különböztethetünk meg. Az első az ellenőrző (validating) szemantikus predikátumok esete, melyek logikai kifejezések, amik kiértékelése futásidőben történik. Ha a kifejezés hamis, akkor az adott szabály használata meghiúsul (az elemző kivételt dob). A szintaxis a következő: {*kifejezés*}?

Például, ha maximum 4 számból álló sorozatot akarunk értelmezni, használhatjuk az alábbi szabályt:

data: ( b+=BYTE )+ {$b.size()<=4}?
;

ahol egy $b nevű listát építünk a BYTE terminális szimbólumokból, egészen addig, amíg a lista mérete kisebb vagy egyenlő mint 4.

A második eset a "kapcsoló" (gated) szemantikus predikátumok használata, amik egy szabályon belül egy alternatíva használatát kapcsolják ki-be. Ezek szintén logikai kifejezések, melyek kiértékelése futásidőben történik. Ha a kifejezés hamis, akkor az adott alternatíva az elemző számára láthatatlan. Ennek használatára remek példa lehet az SQL nyelv, ahol különböző utasításokkal lehet ki vagy bekapcsolni a nyelv egy adott kiegészítését. Ennek szintaxisa {*kifejezés*}?=>

A "maximum 4 elem illesztése" megszorítást kapcsoló predikátumokkal is megoldhatjuk:

data
@init {int n=1;}
: ( {n<=4}?=> BYTE {n++;} )+
;

ahol az @init részben deklarálunk egy n nevű lokális változót deklarálunk. A BYTE alternatíva csak akkor látszódik, ha a kifejezés (n<=4) értéke igaz.

Az utolsó pedig a többértelműséget feloldó szemantikus predikátumok esete. A szintaxis megegyezik az ellenőrző predikátumok szintaxisával. A működésük is igen hasonló. A különbség mindösszesen annyi, hogy a többértelműséget feloldó predikátumok csak akkor jutnak szerephez, ha a szintaxis maga nem egyértelmű.

Az ANTLR által generált LL(*) elemző (az LL(k) elemzők kiegészítése tetszőleges k értékkel) az alternatívák között egy véges automata (DFA) segítségével dönt. A többértelműséget feloldó predikátumok segítik a véges automatát a helyes döntés meghozatalában.


Felhasznált irodalom:
  • Bach Iván: Formális nyelvek (Typotex Kiadó, 2001)
  • Terence Parr: The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Bookshelf, 2007)

ANTLR + StringTemplate

A hétvégén tovább folytattam az ANTLR-el való ismerkedést. Az ANTLR egy parser-generátor, ami egy nyelvtan alapján generál egy rekurzív leszálló (recursive descent) szintaktikai elemzőt.

Egy elemző alapesetben nem csinál mást, minthogy eldönti, hogy egy adott mondat része-e a nyelvnek, szintaktikailag értelmes-e. Az ANTLR segítségével viszont könnyedén készíthetünk olyan nyelvtanokat, amikből a generált parser egy bemenő mondatból kimenetként egy absztrakt szintaxis fát (AST) állít elő. Ezt a fát bejárva akár kiértékelhetjük a bemenetet (utasításokat), akár transzformációkat végezve átfordíthatjuk egy másik nyelvre.

Terence Parr a könyvében azt javasolja, hogy AST-k bejárásához is használjunk nyelvtanokat, pontosabban egy nyelvtan leírásából generált parsert, aminek a bemeneti mondatai az AST-k, kimenete meg mondjuk egy StringTemplate sablon megfelelő kitöltése.

Ezt a folyamatot szemlélteti - szintén Parr könyvéből származó - ábra:

A Code Generation honlapján 2005-ben megjelent egy cikk, "Language Translation Using ANTLR and StringTemplate" címmel, amiben Terence Parr bemutatja, hogyan lehet egy fordítót készíteni, ami egy C-szerű nyelvből Java, Python, vagy Java bytecode kódot állít elő. A példa nyelvtanok sajnos még az ANTLR 2-es verziójával készültek, amit azóta több szempontból is felülmúlt a legújabb, 3-as verzió. A hétvégén ezt alakítottam át, hogy működjön a 3-as verzióval, és először egy AST-t generáljon, majd az AST-t bejárva töltse ki a sablonokat.

A nyelvtanok itt találhatóak:
A tesztelést megvalósító Java kód:
Működése pedig:

A teszt bemenet legyen a honlapon is fentlevő kódrészlet:

char c;
int x;
int foo(int y, char d) {
int i;
for (i=0; i<3; i=i+1) {
x=3;
y=5;
}
}

Ezt Python-ra fordítva az alábbi kimenetet kapjuk:

kelda@psycho:~/ANTLR/cminus$ cat input.txt | java Test
(GLOBALS (VAR char c)) (GLOBALS (VAR int x)) (FUNCTIONS
(FUNCTION int foo (PARAMS (PARAM int y) (PARAM char d))
(BLOCK (LOCALS (VAR int i)) (STATEMENTS (FOR (INIT (= i
0)) (COND (< i 3)) (STMT (= i (+ i 1))) (BLOCK LOCALS
(STATEMENTS (= x 3) (= y 5))))))))

def foo(y, d):
i = 0
while ( i < 3 ):
x = 3
y = 5
i = i + 1

Az elején az AST látható (gyökér levél1 levél2 ... levéln) formában, utána pedig a kimenet.

Ez a bejegyzés igazából nem akart másról szólni, minthogy illusztrálja az ANTLR és a StringTemplate erejét, a részletek bemutatása nélkül. Később részletesen is bemutatom majd a működését, a nyelvtanok felépítését, és hogy az egész hogyan is illeszkedik a blogomba :)

Egy kis segítségként annyit elárulhatok, hogy az SQL Developer egyik könyvtárában van egy oracle.sqldeveloper.migration.translation.sqlserver.jar file, aminek a belsejében található egy generic.stg és egy tsql.stg StringTemplate sablon. Azaz az SQL Developer Migration Workbench is az ANTLR és a StringTemplate segítségével végzi a nyelvi fordítást.

Diplomaterv

Ebben a félévben főleg a diplomatervem elkészítésével fogok foglalkozni, melynek címe:

Adatbázis migráció Microsoft SQL Server adatbázisról Oracle adatbázis-kezelőre

azaz az előző féléves önálló labor témámat folytatom. A diplomaterv kiírásom pontjai:
  1. Ismertesse az adatbázisok migrációinak általános problémáit a hazai és nemzetközi szakirodalom alapján!
  2. Mutassa be a Microsoft SQL Server 2005 és Oracle 10g közötti migrációs eljárások és megoldások lehetőségeit és korlátait!
  3. Készítsen funkcionális tervet a T-SQL nyelven írt üzleti logika szemantikailag helyes PL/SQL nyelvre fordítására a nyelvek kifejezőerejéből adódó korlátok között!
  4. Implementálja és értékelje az elkészített rendszer egyes komponenseit, különös tekintettel
    • a PL/SQL kód olvashatóságára,
    • a PL/SQL kód szintaktikai helyességére,
    • a fordítási hibák jellegére és arányára.
Az első pont egy kicsit trükkös, ugyanis eddig igen kevés releváns szakirodalmat sikerült találnom. Amit találtam, azok is főleg az adatok migrációjával foglalkozik. Ezek közül kiemelendő Erik Peter Bansleben diplomamunkája az esettanulmány hasonlósága miatt:
The source systems consisted of a combination of Microsoft SQL Server and Access databases, while the target platform was an Oracle 9i server which was to serve as the backend database for the new system.
Ha esetleg tud valaki a témába vágó irodalomról, megköszönném, ha jelezné akár hozzászólásban, akár e-mailben: orveny kukac gmail pont com. Nagy segítség lenne :)

Az előző félévben gyakorlatilag a második ponttal foglalkoztam, így úgy érzem, ez nem fog különösebb kihívást jelenteni.

Ami érdekesebb, az a 3. és 4. pont: gyakorlatilag egy Transact-SQL -> PL/SQL fordítót kell készítenem. Szerencsére rátaláltam az ANTLR framework-re, aminek segítségével egy nyelvtanból viszonylag könnyedén lehet szintaktikus elemzőket, fordítókat készíteni.

A honlapján megtalálható Dermott O'Neill-től, az SQL Developer egyik fejlesztőjétől egy idézet:
The decision to use Antlr and StringTemplate for Oracles next generation Migration and SQL Developer features was easy due to the fantastic support on the forums, extensive documentation and great tools. In particular, the ability to parse trees and define target languages using StringTemplate, provided the end to end language translation technology we required. Other parser generators left us high and dry with only half the solution.
Egyelőre még csak az irodalom feldolgozás fázisában tartok, de mindenesetre igen bíztató, hogy egy olyan eszköz használatát tanulom, amivel az SQL Developer Migration Workbench is működik :)