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)