V. DB2/2 REXX

Автор: Kádár Zsolt

Дата: 06.06.1999

Источник: https://xenia.sote.hu

Язык: Венгерский

A DB2/2 REXX interfésze

A többi IBM-es termékhez hasonlóan a DB2/2 adatbázis-kezelô program is rendelkezik REXX interfésszel. A DB2/2 szolgáltatásait a REXX programok háromféle módon használhatják:

  • Az SQLDBS függvény, amely segítségével a REXX programok a parancssorban is kiadható DB2/2 függvényeket hívhatják meg.
  • Az SQLEXEC függvény, amely segítségével a REXX programok SQL parancsokat adhatnak ki.
  • A DB2/2 által nyilvántartott adatstruktúrák, amelyek a REXX programokból is kiolvashatóak.

A lecke folyamán áttekintjük a DB2/2 fent említett REXX felületeit. Mielôtt azonban ebbe belefognánk, tekintsük meg, hogy hogyan kell a REXX programokban regisztrálni az SQLDBS és SQLEXEC függvényeket:

Call RxFuncAdd 'SQLDBS' , 'SQLAR', 'SQLDBS'
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Talán nem felesleges megemlíteni, hogy az adatbázishoz való hozzáférés jogát (a Lan Server jogokhoz hasonlóan) az UPM (User Profile Management) programban eltárolt adatok szabályozzák. Az adatbázist lokálisan használni akaró egyénnek (vagyis a REXX program futtatójának) elôször be kell jelentkeznie az UPM-ben nyilvántartott adatok alapján, különben az adatbázist nem lehet majd elérni. Távoli gépen elhelyezkedô adatbázis elérése esetén a program futtatójának szintén rendelkeznie kell a távoli gépre vonatkozó jogosultságokkal. Ha elfelejtünk belépni az adatbázis használata elôtt, akkor a DB2 kérni fogja a belépést, mielôtt végrehajtaná utasításainkat.

Adatbázis adminisztráció

A már említett SQLDBS és SQLEXEC függvények segítségével a REXX programokkal elvégeztethetjük az adatbázisok adminisztrációját. Kiszolgáló esetén az adminisztráció a kliensek elérési jogainak nyilvántartását jelenti. A jogok kiadása és visszavonása a DB2 Query Manager komponensével, vagy REXX programokba ágyazott SQL utasításokkal végeztethetôk el. Mielôtt azonban ezeket a REXX programokat futtatni tudnánk, rendszergazda jogokkal kell rendelkeznünk, mivel csak ô adhat ki ilyesfajta utasításokat. A továbbiakban két programrészletet mutatunk be, amelyek azt demonstrálják, hogy hogyan lehet REXX-bôl a kliensek elérési jogosultságát megadni, vagy pedig visszavonni.

/* Jogosultság megadása az adatbázis eléréséhez ***************************/
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Say 'Adja meg az adatbázis nevét!'
pull dbname    /* az adatbázis neve */
Say 'Adja meg a felhasználó nevét!'
pull authname  /* a munkaállomás neve, amelynek a jogot akarjuk adni */

/* Elôször kapcsolódni kell az adatbázishoz */
Call SQLEXEC 'CONNECT TO ' dbname ' IN SHARE MODE';
IF ( SQLCA.SQLCODE <> 0) THEN
	DO
		Say 'Nem sikerült a kapcsolódás az adatbázishoz!'
   		EXIT
 	END
 
/* A GRANT utasítást nem lehet közvetlenül kiadni REXX-bôl.       */
/* Csak az EXECUTE IMMEDIATE kulcsszavak kíséretében használható. */
stmt1 ='GRANT CONNECT ON DATABASE TO ' authname
Call SQLEXEC 'EXECUTE IMMEDIATE :stmt1';
 
IF ( SQLCA.SQLCODE <> 0) THEN
   	Say 'Nem sikerült az elérési jog megadása!'
ELSE
  	Say 'Az elérési jog megadása sikerült!'
 
/* Lezárjuk a kapcsolatot az adatbázissal. */
Call SQLEXEC 'CONNECT RESET';
 
EXIT
 

/* Az adatbázis elérési jogok megvonása ***********************************/
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Say 'Adja meg az adatbázis nevét!'
pull dbname    /* az adatbázis neve */
Say 'Adja meg a felhasználó nevét!'
pull authname  /* a munkaállomás neve, amelynek a jogát meg akarjuk vonni */
 
/* Elôször kapcsolódni kell az adatbázishoz */
Call SQLEXEC 'CONNECT TO ' dbname ' IN SHARE MODE';
IF ( SQLCA.SQLCODE <> 0) THEN
	DO
		Say 'Nem sikerült a kapcsolódás az adatbázishoz!'
   		EXIT
 	END
 
/* A REVOKE utasítást nem lehet közvetlenül kiadni REXX-bôl.      */
/* Csak az EXECUTE IMMEDIATE kulcsszavak kíséretében használható. */
stmt1 ='REVOKE CONNECT ON DATABASE FROM ' authname
Call SQLEXEC 'EXECUTE IMMEDIATE :stmt1';
 
IF ( SQLCA.SQLCODE <> 0) THEN
	Say 'Nem sikerült az elérési jog visszavonása!'
ELSE
  	Say 'Az elérési jog visszavonása sikerült!'
 
/* Lezárjuk a kapcsolatot az adatbázissal. */
Call SQLEXEC 'CONNECT RESET';
 
EXIT

Figyeljük meg, hogy a GRANT és REVOKE parancsok csak az EXECUTE IMMEDIATE kíséretében hajthatók végre az SQLEXEC függvénnyel. A késôbbiekben még részletesen lesz arról szó, hogy mely SQL parancsokat kell így végrehajtani, és mely parancsokat lehet egy az egyben megadni.

Ha egy felhasználó hozzáférhet egy adott adatbázishoz, akkor az még nem jelenti azt, hogy az adatbázisban található táblákhoz és nézetekhez is hozzáférhet. Ezeket a jogokat ugyanis külön korlátozza a DB2. Az alábbiakban megadtunk két (az elôbbiekhez nagyon hasonló) példaprogramot, amelyek a paraméterként megadott kliensek tábla-hozzáférési jogait állítják be.

/* Táblaelérési jogok megadása ********************************************/
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Say 'Adja meg az adatbázis nevét!'
pull dbname    /* az adatbázis neve */
Say 'Adja meg a felhasználó nevét!'
pull authname  /* a munkaállomás neve, amelynek a jogát meg akarjuk vonni */
Say 'Adja meg a tábla nevét!'
pull tablename /* a tábla neve */

/* Elôször kapcsolódni kell az adatbázishoz */
Call SQLEXEC 'CONNECT TO ' dbname ' IN SHARE MODE';
IF ( SQLCA.SQLCODE <> 0) THEN
	DO
		Say 'Nem sikerült a kapcsolódás az adatbázishoz!'
   		EXIT
 	END

/* A GRANT utasítást nem lehet közvetlenül kiadni REXX-bôl.      */
/* Csak az EXECUTE IMMEDIATE kulcsszavak kíséretében használható. */
stmt1 ='GRANT SELECT ON TABLE ' tablename ' TO ' authname
Call SQLEXEC 'EXECUTE IMMEDIATE :stmt1';

IF ( SQLCA.SQLCODE <> 0) THEN
   	Say 'Nem sikerült az elérési jog megadása!'
ELSE
  	Say 'Az elérési jog megadása sikerült!'
 
/* Lezárjuk a kapcsolatot az adatbázissal. */
Call SQLEXEC 'CONNECT RESET';
 
EXIT


/* Táblaelérési jogok megvonása *******************************************/
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Say 'Adja meg az adatbázis nevét!'
pull dbname    /* az adatbázis neve */
Say 'Adja meg a felhasználó nevét!'
pull authname  /* a munkaállomás neve, amelynek a jogát meg akarjuk vonni */
Say 'Adja meg a tábla nevét!'
pull tablename /* a tábla neve */

/* Elôször kapcsolódni kell az adatbázishoz */
Call SQLEXEC 'CONNECT TO ' dbname ' IN SHARE MODE';
IF ( SQLCA.SQLCODE <> 0) THEN
	DO
		Say 'Nem sikerült a kapcsolódás az adatbázishoz!'
   		EXIT
 	END

/* A REVOKE utasítást nem lehet közvetlenül kiadni REXX-bôl.     */
/* Csak az EXECUTE IMMEDIATE kulcsszavak kíséretében használható. */
stmt1 ='REVOKE SELECT ON TABLE ' tablename ' FROM ' authname
Call SQLEXEC 'EXECUTE IMMEDIATE :stmt1';

IF ( SQLCA.SQLCODE <> 0) THEN
	Say 'Nem sikerült az elérési jog visszavonása!'
ELSE
  	Say 'Az elérési jog visszavonása sikerült!'
 
/* Lezárjuk a kapcsolatot az adatbázissal. */
Call SQLEXEC 'CONNECT RESET';
 
EXIT

Munkaállomás esetében az adminisztráció az adatbázis- és csomópontkönyvtárakban (node directory) tárolt információ menedzselésébôl áll. Az adatbáziskönyvtár azokról az adatbázisokról tartalmaz információt (adatbázis neve, alias, az adatbázist hordozó munkaállomás neve, az adatbázis típusa, stb.) amelyeket a munkaállomás használhat. Amikor a DB2 érzékeli, hogy használni akarják valamelyik adatbázist, akkor ebben a könyvtárban tárolt (katalogizált) információ alapján dönti el, hogy merre kell az adatbázist keresni. Amennyiben az adatbázis egy másik csomóponton van, akkor a csomópont könyvtárban is keresni kell, hogy a DB2 meg tudja állapítani a kapcsolatfelvételhez szükséges adatokat (az adatbázist hordozó munkaállomás neve, a hálózati adapter paraméterei, a használt protokoll, stb.). Magától értetôdô, hogy ezeket a könyvtárakat frissíteni kell, ha új adatbázisokat hozunk létre, vagy régieket szüntetünk meg. Az alábbi példaprogramok azt mutatják be, hogy hogyan kell REXX-bôl a könyvtárak tartalmát (vagyis a katalógust) frissíteni.

/* APPC csomópont katalogizálása ******************************************/
Call RxFuncAdd 'SQLDBS', 'SQLAR', 'SQLDBS'
 
Say 'Adja meg a csomópont nevét!'
Pull nodename
Say 'Adja meg a lokális LU nevét!'    /* CM/2 SNA munkaállomás neve */
Pull locallu
Say 'Adja meg a partner LU nevét!'    /* CM/2 SNA szerver neve      */
Pull partnerlu
Say 'Adja meg az SNA transzmissziós szolgáltatás módját!'
Pull mode
 
/* A parancs összerakása */
SQL_EndString = 'LOCAL 'locallu 'REMOTE 'partnerlu 'MODE 'mode
SQL_String = 'CATALOG APPC NODE 'nodename SQL_EndString
Call SQLDBS SQL_String
IF SQLCA.SQLCODE = 0 THEN
	Say 'A csomópont katalogizálása sikerült.'
ELSE 
     	Say 'A csomópont katalogizálása nem sikerült! Hibakód: 'SQLCA.SQLCODE
 
EXIT
 

/* APPC csomópont eltávolítása a katalógusból *****************************/
Call RxFuncAdd 'SQLDBS', 'SQLAR', 'SQLDBS'
 
Say 'Adja meg a csomópont nevét!'
Pull nodename

Call SQLDBS 'UNCATALOG NODE 'nodename
IF SQLCA.SQLCODE = 0 THEN
     	Say 'A csomópont eltávolítása sikerült!'
ELSE
     	Say 'A csomópont eltávolítása nem sikerült! Hibakód: 'SQLCA.SQLCODE

EXIT


/* Távoli adatbázis katalogizálása ****************************************/
Call RxFuncAdd 'SQLDBS', 'SQLAR', 'SQLDBS'

Say 'Adja meg az adatbázis nevét!'
Pull dbname
Say 'Adja meg az alias-t!'
Pull alias
Say 'Adja meg a csomópont nevét, ahol katalogizálni akarja az adatbázist!'
Pull node
 
/* A parancs összerakása */
IF alias = "" THEN
     	SQL_String = 'CATALOG DATABASE 'dbname  'AT NODE 'node
ELSE
     	SQL_String = 'CATALOG DATABASE 'dbname 'AS 'alias ' AT NODE 'node
 
/* A parancs kiadása */
Call SQLDBS SQL_String

IF SQLCA.SQLCODE = 0 THEN
     	Say 'Az adatbázis katalogizálása sikerült!'
ELSE
     	Say 'Az adatbázis katalogizálása nem sikerült! Hibakód: 'SQLCA.SQLCODE

EXIT

 
/* Adatbázis eltávolítása a katalógusból. *********************************/
Call RxFuncAdd 'SQLDBS', 'SQLAR', 'SQLDBS'

Say 'Adja meg az adatbázis nevét!'
Pull dbname

Call SQLDBS 'UNCATALOG DATABASE 'dbname
IF SQLCA.SQLCODE = 0 THEN
     	Say 'Az adatbázis eltávolítása a katalógusból sikerült!'
ELSE
     	Say 'Az adatbázis eltávolítása nem sikerült! Hibakód: 'SQLCA.SQLCODE

EXIT

SQL parancsok kiadása REXX-bôl

A jogosultságok megadásán kívül egyéb SQL parancsokat (pl. táblázatok létrehozása, új sorok létrehozása, meglévô sorok frissítése stb.) is végre lehet hajtani. Azt lehet mondani, hogy az összes dinamikus SQL parancs használható REXX-bôl is. A statikus (elôre elkészített) SQL parancsoknak a program futtatása elôtt már rendelkezésre kell állniuk, ezért ezek nem használhatók REXX-bôl, mivel ez egy interpretált nyelv.

Az egyik legfontosabb és leggyakrabban használt SQL parancs a SELECT, amellyel az adatbázisban tárolt adatok egy részhalmazának kiválasztását lehet elvégezni. A SELECT használatát egy példa keretein belül fogjuk bemutatni. A példaprogramban a következô mûveleteket fogjuk elvégezni: Kapcsolódunk az adatbázishoz, megalkotjuk a SELECT utasítást, elkészítjük a dinamikus SQL parancsot, definiáljuk és kapcsoljuk a kurzort a SELECT parancshoz, inicializáljuk a kurzort, beolvassuk a sorokat, zárjuk a kurzort és megerôsítjük az elvégzett változtatásokat.

/* Adatbázisban található táblázatok listázása ****************************/
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Say 'Adja meg az adatbázis nevét!'
Pull dbname
  
/* Kapcsolódás az adatbázishoz */
Call SQLEXEC 'CONNECT TO ' dbname ' IN SHARE MODE' 
IF ( SQLCA.SQLCODE <> 0) THEN 
	Call Error_handling 1

/* Az SQL parancs elôállítása */
st = "SELECT name, creator FROM sysibm.systables WHERE creator <> ? AND creator <> ?" 
Call SQLEXEC 'PREPARE S1 FROM :st' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 2

/* A kurzor létrehozása */  
Call SQLEXEC 'DECLARE C1 CURSOR FOR S1' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 3

/* Rendszertáblázatokon kívül minden más táblázatra megnyitjuk a kurzort */
parm_var1= "SYSIBM"
parm_var2= "QRWSYS"
Call SQLEXEC 'OPEN C1 USING :parm_var1, :parm_var2' 

/* Az elsô olvasás */
Call SQLEXEC 'FETCH C1 INTO :table_name, :creator_name'   
IF ( SQLCA.SQLCODE <> 0) THEN 
	Call Error_handling 4
  
/* Amíg csak lehet olvasunk és megjelenítjük a kapott adatokat */
DO WHILE SQLCA.SQLCODE = 0
    	IF SQLCA.SQLCODE = 0 THEN
		Say 'Tábla/Készítô: 'table_name'/'creator_name
	Call SQLEXEC 'FETCH C1 INTO :table_name, :creator_name'  
END
 
/* Zárjuk a kurzort */
Call SQLEXEC 'CLOSE C1' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 5

/* Megerôsítjük a változtatásokat */
Call SQLEXEC 'COMMIT' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 6

/* Lezárjuk az adatbázissal a kapcsolatot */
Call SQLEXEC 'CONNECT RESET' 
IF ( SQLCA.SQLCODE <> 0) THEN 
	Call Error_handling 7

EXIT

/* Hibakezelés */
Error_handling:
	code = arg(1)
	Say 'Hiba történt! Rutin: 'code' Hibakód: 'SQLCA.SQLCODE
	SAY SQLCA.SQLERRMC
	Call SQLEXEC 'CONNECT RESET' 
	EXIT
RETURN

Figyeljük meg, hogy a táblák neve két szóból áll, amelyeket egy pont választ el egymástól. Az elsô tag a tábla készítôjének a nevét tartalmazza. A második tag a tábla rövidített neve. Ha nem használjuk a készítô nevét, akkor a DB2/2 mindig a lokális gépen fogja keresni a táblázatot. Amikor a kérdéses táblázat egy távoli gépen helyezkedik el, akkor mindig a teljes nevet kell használni! Azt is érdemes megfigyelni, hogy a SELECT parancs összeállításakor kérdôjeleket írtunk be két, akkor még ismeretlen változó helyébe. A kérdôjelek értéke csak akkor vált ismertté, amikor megnyitottuk a kurzort. Ezáltal értük el, hogy bizonyos, az SQL parancs elôkészítésekor még nem ismert (jelen esetben rendszer) táblázatokat kihagyjunk a listázásból. Jó ha azt is tudjuk, hogy az SQL parancsot (S1) és a kurzort megtestesítô változók sorszámának az 1-tôl 100-ig terjedô tartományban kell maradnia. A C51...C100 változókat pedig csak a WITH HOLD opcióval együtt használhatjuk.

Az elôzô példában kihasználtuk azt, hogy tudtuk, hogy a kiolvasott táblázat sorainak elsô két oszlopa tartalmazza a tábla nevét és készítôjét. A gyakorlatban viszont inkább olyan problémákkal szembesülünk, amikor nem ismerjük elôre a feldolgozott táblázat struktúráját. Ilyenkor szokták alkalmazni a változó listájú SELECT utasítás technikáját, amelyet a következô példaprogramunkban mutatunk be.

/* A változó listájú SELECT utasítás technikája ************************/
/* táblázatok kiolvasására *********************************************/
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Say 'Adja meg az adatbázis nevét!'
Pull dbname
Say 'Adja meg a készítô nevét!'
Pull creator
Say 'Adja meg a tábla nevét!'
Pull tablename
  
/* Kapcsolódás az adatbázishoz */
Call SQLEXEC 'CONNECT TO ' dbname ' IN SHARE MODE' 
IF ( SQLCA.SQLCODE <> 0) THEN 
	Call Error_handling 1

/* Az SQL parancs összeállítása */
st1 = "SELECT * FROM "creator||'.'||tablename

/* A kurzor létrehozása */  
Call SQLEXEC 'DECLARE C1 CURSOR FOR S1' 
IF SQLCA.SQLCODE <> 0 THEN
 	Call Error_handling 2

/* Az SQL parancs elôkészítése */
Call SQLEXEC 'PREPARE s1 INTO :sqldavar FROM :st1'
IF SQLCA.SQLCODE <> 0 THEN
	Call Error_handling 3

/* Kiolvassuk az oszlopinformációt */
Call SQLEXEC 'DESCRIBE s1 INTO :sqldavar'
IF SQLCA.SQLCODE <> 0 THEN
 	Call Error_handling 4

/* Megnyitjuk a kurzort */
Call SQLEXEC 'OPEN C1'
IF SQLCA.SQLCODE <> 0 THEN
 	Call Error_handling 5

/* Az elsô olvasás */
Call SQLEXEC 'FETCH c1 USING DESCRIPTOR :sqldavar'
IF SQLCA.SQLCODE <> 0 THEN 
	Call Error_handling 6

/* Amíg csak lehet olvasunk és megjelenítjük a kapott adatokat */
DO WHILE SQLCA.SQLCODE = 0

	/* Végiglépkedünk a sorban lévô oszlopokon */
     	outrec = ''
     	DO col = 1 TO sqldavar.sqld
		/* Üres mezô */
        	IF sqldavar.col.sqlind = -1 THEN 
       			outrec = outrec CENTER('-', sqldavar.col.sqllen)
        	ELSE
        		outrec = outrec sqldavar.col.sqldata
     	END /* do */

	/* Kiíratjuk az eredményt */
     	Say outrec

	/* Ismét olvasunk */
     	Call SQLEXEC 'FETCH c1 USING DESCRIPTOR :sqldavar'
END /* do */

/* Zárjuk a kurzort */
Call SQLEXEC 'CLOSE C1' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 7

/* Megerôsítjük a változtatásokat */
Call SQLEXEC 'COMMIT' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 8

/* Lezárjuk az adatbázissal a kapcsolatot */
Call SQLEXEC 'CONNECT RESET' 
IF ( SQLCA.SQLCODE <> 0) THEN 
	Call Error_handling 9

EXIT

/* Hibakezelés */
Error_handling:
	code = arg(1)
	Say 'Hiba történt! Rutin: 'code' Hibakód: 'SQLCA.SQLCODE
	SAY SQLCA.SQLERRMC
	Call SQLEXEC 'CONNECT RESET' 
	EXIT
RETURN

Mint már említettük, a változó listájú SELECT utasítás technikáját akkor szoktuk többek között alkalmazni, amikor nem tudjuk, hogy hány oszlopot tartalmaz a kiolvasandó táblázat, illetve nem ismerjük az oszlopok neveit. Szerencsére a DB2-ben rendelkezésre áll az SQLDA adatstruktúrát, amely információt tartalmaz az aktuális táblázat oszlopairól. Miután a példaprogramban hozzákapcsolódtunk az adatbázishoz, megalkotjuk az SQL parancsot, amelyet aztán a kurzor definiálása követ. Következô lépésként elôkészítjük az SQL parancsot, amelynek INTO opciójával megadjuk annak a változónak a nevét, amelyben az SQLDA-ból kiolvasott táblainformációt akarjuk tárolni. A DESCRIBE parancs hatására kerül be az oszlopinformáció az sqldavar változóba, amelyet aztán a kurzor megnyitása után felhasználunk az adatok kiolvasásához (FETCH). Az összes adat kiolvasása után zárjuk a kurzort és megerôsítjük az elvégzett változtatásokat.

A DB2 által nyilvántartott adatstruktúrákról még részletesen lesz szó. Az SQLDA-ról most csak annyit árulunk el, hogy öt elembôl áll, amelyekbôl négyet használtunk a példaprogramban. Az SQLLEN elem az aktuális oszlop hosszát tartalmazza. Az SQLIND elem jelzi, ha üres mezôvel állunk szemben. Az SQLDATA elem tartalmazza az aktuális sor oszlopában tárolt értéket, az SQLDA.SQLD pedig a sorban található oszlopok számát.

A táblázatokat persze elôbb fel kell tölteni adattal, mielôtt keresni tudnánk bennük. Az SQL parancsokkal természetesen létre lehet hozni új sorokat, illetve lehet módosítani már meglévô sorok tartalmát. Magától értetôdôen lehetséges a felesleges sorok törlése is. Ezekhez a mûveletekhez persze rendelkezni kell a megfelelô jogosultságokkal.

Az alábbi kódrészlet azt mutatja be, hogy hogyan lehet új sort beilleszteni az INSERT utasítás segítségével:

creator = 'WORKID'				  /* a létrehozó neve */
tbname 	= 'BASEBALL'				  /* a tábla neve     */
fields 	=  lastname||','||firstname||','||middle  /* az oszlopok neve */
values 	= 'MANTLE' ||','||'MICKEY' ||','||'C'     /* az új értékek    */

/* Az INSERT parancs összeállítása */  
st1 = "INSERT INTO "creator||'.'||tbname "("fields") VALUES ("values")"

/* Az INSERT parancs végrehajtása */
Call SQLEXEC 'EXECUTE IMMEDIATE :st1'
IF SQLCA.SQLCODE <> 0 THEN Say 'Hiba történt!'

Már meglévô sorok megváltoztatása az UPDATE utasítással történhet. A sorokat lehet egyszerre (tömegesen), vagy pedig soronként lépkedve frissíteni. Tömeges frissítés esetén egyetlen UPDATE-tal hajtjuk végre a változtatásokat azokon a sorokon, amelyekre illik a WHERE kulcsszó paramétereként megadott leírás. Az alábbi kódrészletben egyetlen utasítással cseréljük ki a "department" oszlopban eltárolt adatot:

creator	 = 'WORKID'				/* a létrehozó neve */
tbname	 = 'EMPLOYEE'				/* a tábla neve     */
old_dept = '951A'				/* régi érték       */
new_dept = '884A'				/* új érték         */

/* Az UPDATE parancs összeállítása */ 
st1 = 'UPDATE 'creator||'.'||tbname' SET DEPT='||new_dept' WHERE DEPT='||old_dept

Call SQLEXEC 'EXECUTE IMMEDIATE :st1'
IF SQLCA.SQLCODE <> 0 THEN Say 'Hiba történt!'

Bizonyos esetekben viszont arra van szükség, hogy sorról sorra lépkedve frissítsük az adatokat. Tipikus példa erre az az alkalmazás, amely a felhasználótól folyamatosan bekért adatokat írja be a táblázatba. Mint a korábbi SELECT példában már láttuk, az SQLDA struktúra nagyon hasznos lehet, ha például az aktuális sor paramétereire van szükség. Ugyancsak jól jön az SQLDA, ha ellenôrizni kell a felhasználó által bevitt adatokat, mielôtt a frissítést végrehajtanánk, bár ez nem kötelezô. Az alábbi, ebben a leckében utolsó komplett példaprogramban a lépésrôl lépésre történô frissítésre láthatunk példát:

/* Táblázat sorainak frissítése lépésrôl lépésre ***********************/
Call RxFuncAdd 'SQLEXEC', 'SQLAR', 'SQLEXEC'

Say 'Adja meg az adatbázis nevét!'
Pull dbname
Say 'Adja meg a készítô nevét!'
Pull creator
Say 'Adja meg a tábla nevét!'
Pull tablename
Say 'Adja meg az elsô oszlop nevét!'
Pull column1
Say 'Adja meg a második oszlop nevét!'
Pull column2
Say 'Adja meg a harmadik oszlop nevét!'
Pull column3
Say 'Adja meg a harmadik oszlopban keresett értéket!'
Pull data

/* Kapcsolódás az adatbázishoz */
Call SQLEXEC 'CONNECT TO ' dbname ' IN SHARE MODE' 
IF SQLCA.SQLCODE <> 0 THEN 
	Call Error_handling 1

/* Az SQL parancs összeállítása */
st1 ="SELECT "column1||', '||column2" FROM USERID.EMP_PHOTO WHERE "column3||"='"data"' FOR UPDATE OF "column2
say st1
pause

/* A kurzor létrehozása */  
Call SQLEXEC 'DECLARE C1 CURSOR FOR S1' 
IF SQLCA.SQLCODE <> 0 THEN
 	Call Error_handling 2 

/* Az SQL parancs elôkészítése */
Call SQLEXEC 'PREPARE s1 FROM :st1'
IF SQLCA.SQLCODE <> 0 THEN
	Call Error_handling 3

/* Megnyitjuk a kurzort */
Call SQLEXEC 'OPEN C1'
IF SQLCA.SQLCODE <> 0 THEN
 	Call Error_handling 4

/* Az elsô olvasás */
Call SQLEXEC 'FETCH c1 INTO :var1, :var2'
IF SQLCA.SQLCODE <> 0 THEN 
	Call Error_handling 5

DO WHILE SQLCA.SQLCODE = 0    	/* Végiglépkedünk a választott sorokon. */

   	/* Megmutatjuk a jelenlegi értékeket és bekérjük az újat. */
   	Say column1'='var1', ' column2'='var2
   	Say 'Adja meg a 'column2' új értékét!'
   	Pull newvalue

   	/* �sszerakjuk a frissítéshez szükséges parancsot. */
   	updt = 'UPDATE 'creator||'.'||tablename' SET ' column2||"='"||newvalue||"'"|| 'WHERE CURRENT OF c1'

	/* Végrehajtjuk a frissítést. */
  	Call SQLEXEC 'EXECUTE IMMEDIATE :updt'
   	IF SQLCA.SQLCODE <> 0 THEN 
		Call Error_handling 6

	/* Beolvassuk a következô sort. */
   	Call SQLEXEC 'FETCH c1 INTO :var1, :var2'
END /* do */
 
/* Zárjuk a kurzort */
Call SQLEXEC 'CLOSE C1' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 7

/* Megerôsítjük a változtatásokat */
Call SQLEXEC 'COMMIT' 
IF ( SQLCA.SQLCODE <> 0) THEN
 	Call Error_handling 8

/* Lezárjuk az adatbázissal a kapcsolatot */
Call SQLEXEC 'CONNECT RESET' 
IF ( SQLCA.SQLCODE <> 0) THEN 
	Call Error_handling 9

EXIT

/* Hibakezelés */
Error_handling:
	code = arg(1)
	Say 'Hiba történt! Rutin: 'code' Hibakód: 'SQLCA.SQLCODE
	SAY SQLCA.SQLERRMC
	Call SQLEXEC 'CONNECT RESET' 
	EXIT
RETURN

A program elôször bekéri az adatbázis, a frissítendô táblázat és az azt létrehozó személy nevét. A példa három oszlopból álló táblázat sorainak frissítését végzi. Az oszlopok neveinek megadása után bekérjük azt az adatot, amelynek a harmadik oszlopban történô elôfordulása esetén aktivizáljuk a második oszlop tartalmának megváltoztatását. Az adatbázishoz történô kapcsolódás után összeállítjuk a SELECT parancsot, majd pedig definiáljuk a kurzort. Az SQL parancs elôkészítése után megnyitjuk a kurzort és beolvassuk az elsô sort, amelynek harmadik oszlopa a megadott értéket tartalmazza. Megjelenítjük az elsô és a második oszlop tartalmát, és kérjük a felhasználót, hogy adja meg a második oszlop új értékét. Ezek után elvégezzük az aktuális sor frissítését. A beolvasást és frissítést egészen addig ismételjük, amíg csak vannak olyan sorok, amelyek a választási kritériumnak eleget tesznek. A program végén a megszokott zárási mûveleteket hajtjuk végre.

Az elôzôekben már volt arról szó, hogy egyes SQL parancsokat közvetlenül, másokat pedig az EXECUTE IMMEDIATE kulcsszavak kíséretében kell használni. Erre azért van szükség, mert bizonyos parancsokat elô kell készíteni végrehajtás elôtt. Az alábbiakban felsoroltuk azokat a parancsokat, amelyeket közvetlenül lehet az SQLEXEC-kel végrehajtatni. Az összes többi, itt nem szereplô parancsot az EXECUTE IMMEDIATE kíséretében lehet csak használni. Természetesen arra is lehetôség van, hogy ezeket a parancsokat két lépésben alkalmazzuk (elôkészítés a PREPARE paranccsal, majd pedig végrehajtás az EXECUTE-tal).

Közvetlenül végrehajtható SQL parancsok:

  • CLOSE
  • COMMIT
  • CONNECT
  • DECLARE
  • DESCRIBE
  • EXECUTE
  • EXECUTE IMMEDIATE
  • FETCH
  • OPEN
  • PREPARE
  • ROLLBACK

SQL adatstruktúrák

A példaprogramok során már használtuk a DB2/2 által rendelkezésre bocsátott adatstruktúrák egy részét. Az alábbi táblázatban felsoroltuk az összes, REXX programból használható, elôre definiált SQL változót.

SQL struktúra: Funkció:
SQLCA (SQL Communication Area) Információ a végrehajtott parancsokról (hibakód, hibaüzenetek)
SQLDA (SQL Descriptor Area) Információ dinamikus SQL parancsokhoz (elemek típusa, mérete, értéke, stb.)
SQLCHAR Információ a kliensek által küldött adatról (A Database Application Remote Interface használja)
SQLOPT SQL opciók (dátum/idô formája, elválasztási szint, blokkolás, stb.)
SQLEDINFO DB2 programinformációk (adatbázis neve, alias, meghajtó, alkönyvtár, csomópont neve, programverzió, kódlap, stb.)
SQL_DIR_ENTRY Könyvtárinformációk (lokális és cél adatbázis neve, kódlap, a kliens alkalmazás neve, stb.)
SQLENINFO Hálózati (csomópont) információk (csomópont neve, LU-ok, mód, protokoll, kódlap, adapter, stb.)
SQLESYSTAT Rendszer és DB2 statisztikák (idôzóna, idôpont, termék verziója és fixpak szintje, legutolsó backup idôpontja, stb.)
SQLEDBSTAT Ugyanaz, mint az SQLESYSTAT
SQLEUSRSTAT Felhasználói statisztikák (tranzakciók száma, olvasások száma, authorizációs adatok, stb.)
SQLDCOL Oszlopinformációk (az oszlopok kezdete és vége)
SQL_AUTHORIZATIONS Authorizációs paraméterek nyilvántartása

A továbbiakban részletesen is bemutatjuk a két leggyakrabban használt struktúra, az SQLCA és SQLDA felépítését.

Az SQLCA felépítése:

Mezô: Jelentés:
SQLCA.SQLCODE SQL hibakód
SQLCA.SQLERRML A hibaüzenet (SQLERRMC) hossza
SQLCA.SQLERRMC Hibaüzenet
SQLCA.SQLERRP Diagnosztikai információ
SQLCA.SQLERRD.i (i = 1..6) Diagnosztikai információ
SQLCA.SQLWARN.i (i = 0..10) Figyelmeztetô flagek
SQLCA.SQLSTATE SQL állapotkód

Az SQLDA felépítése:

Mezô: Jelentés:
SQLDA.SQLD Az SQLVAR elemek száma
SQLDA.n.SQLTYPE Az n. elem típusa
SQLDA.n.SQLLEN Az n. elem hossza
SQLDA.n.SQLDATA Az n. elem értéke
SQLDA.n.SQLIND Üres mezô jelzôje
SQLDA.n.SQLNAME Az oszlop neve

A többi struktúráról (és az SQL parancsok jelentésérôl, szintaktikájáról és használatáról) bôséges információt lehet találni a DB2/2 telepítése után elérhetô segédletekben.

 

REXX GYÍK:

K1. Decimális típusú cella használata esetén az SQLDA.n.SQLLEN nem kap értéket. Miért?
V1. Amennyiben a cella típusa (SQLTYPE) decimális, az SQLLEN maga is összetett változó lesz. Ilyenkor tehát az SQLDA.n.SQLLEN helyett az SQLDA.n.SQLLEN.PRECISION (a tizedespont elôtti rész szélessége) és SQLDA.n.SQLLEN.SCALE (a tizedes pont utáni rész szélessége) változókat kell használni!

 

Gyakorlatok:

1. Készítsen programot, amely kilistázza a paraméterként megadott csomóponton lévô adatbázisokban található nem rendszertáblákat!

Kádár Zsolt
1999. 06. 06.