Embedded SQL Dotazovací Jazyky I Autoři: Bc. CHOMUT Miroslav Bc. VODOLÁN Miroslav
Obsah Jak využívat SQL v programech Historie Příklad Podpora Způsob kompilace Statické SQL Práce s DB Struktura SQLCA Ošetření výjimek Hostitelské proměnné Indikační proměnné Základní SQL příkazy DML Returning Clause Kurzory Skrolovatelné kurzory Dynamické SQL EXECUTE IMMEDIATE PREPARE & EXECUTE SQLDA
Jak využívat SQL v programech Komunikací s DB serverem přes SQL API + Standardní princip programování + Využití všech možností DB serveru Málo přehledný zdrojový kód SQL přímo v kódu aplikace Embedded SQL + Přehledný kód aplikace Nejsou podporovány všechny typy databází
Historie embedded SQL Počátek v polovině 80. let IBM DB2 Standardizace v ANSI SQL 1986 Týká se pouze statického embedded SQL Vzorem existující implementace v DB2 Oracle se liší od ANSI SQL
Příklad embedded SQL v C EXEC SQL INCLUDE sqlca; int main(int argc, char const * argv[]) { EXEC SQL BEGIN DECLARE SECTION; char *username = "Manager"; char *password = "Manager"; EXEC SQL END DECLARE SECTION; //Connect to database EXEC SQL CONNECT :username IDENTIFIED BY :password if(sqlca.sqlcode<0)exit(-1); //Creating table EXEC SQL CREATE TABLE Viewing ( propertyno VARCHAR(5) NOT NULL, clienno VARCHAR(5) NOT NULL, ); if(sqlca.sqlcode>=0){ //routines on success } else{ //unsuccessful creation } } //Commit the transaction and disconnect from the database EXEC SQL COMMIT WORK RELEASE;
Podpora embedded SQL DB2 C/C++, Java, Cobol, Fortran,.. Oracle Pro*C/C++, Pro*FORTRAN, Pro*COBOL, MS SQL Server COBOL prekompiler třetích stran MySQL COBOL prekompiler třetích stran
Způsob kompilace program.ps prekompilace pomocí Pro* C/C++ program.c obvyklá kompilace C/C++ program.o slinkování s Oracle knihovnami pro embedded SQL program spuštění
Statické SQL Dotazy známé již při kompilaci + Nemusí být parsovány a optimalizovány až za běhu + Odolné proti SQL injection Ne vždy si s nimi vystačíme
Statické SQL Prefix EXEC SQL I/O hostitelskými proměnnými Chybové příznaky ve struktuře SQLCA Syntaxe podobná SQL
Práce s DB Připojení k DB ROLLBACK / COMMIT EXEC SQL CONNECT :username IDENTIFIED BY :userpassword; EXEC SQL COMMIT WORK RELEASE; EXEC SQL ROLLBACK WORK RELEASE;
Struktura SQLCA SQL Communications area Stavové informace o provádění příkazů Ošetření chyb přes WHENEVER EXEC SQL INCLUDE SQLCA;
Struktura SQLCA SQLCA sqlcaid (char[8]) sqlabc (long) sqlcode (long) sqlerrm sqlerrml (unsigned short) sqlerrmc (char[70]) sqlerrp (char[8]) sqlerrd (long[6]) sqlwarn (char[8]) sqlext (char[8])
Struktura SQLCA sqlcaid identifikátor "SQLCA" sqlabc délka sqlcaid sqlcode status (0) no error (>0) detekována výjimka obvykle SELECT nebo FETCH vrátí prázdnou množinu (<0) dotaz nevykonán, měl by být ROLLBACK
Struktura SQLCA sqlerrm sqlerrml délka chybové zprávy sqlerrmc text chybové zprávy sqlerrp nevyužito sqlerrd sqlerrd[2] počet zpracovaných řádků sqlerrd[4] pozice chyby v posledním parse error ostatní nevyužito
Struktura SQLCA sqlwarn sqlwarn[0] pokud je nastaven jiný flag sqlwarn[1] došlo ke zkrácení při přirazení sqlwarn[2] NULL sloupec vynechán pro agregovanou funkci sqlwarn[3] inkonzistence v počtu hostitelských proměnných v SELECT sqlwarn[4] UPDATE nebo DELETE bez WHERE sqlwarn[5] neproběhla kompilace pro PL/SQL error sqlwarn[6] nevyužito sqlwarn[7] nevyužito
Ošetření výjimek Explicitní kontrola SQLCA kontrola po každém dotazu WHENEVER příkaz automatická kontrola EXEC SQL WHENEVER <podmínka> <akce> EXEC SQL WHENEVER NOT FOUND DO BREAK
Podmínka SQLERROR Ošetření výjimek WHENEVER sqlcode má negativní hodnotu SQLWARNING sqlwarn[0] má nenulovou hodnotu NOT FOUND dotaz vrátil prázdnou množinu řádků
Ošetření výjimek WHENEVER Akce STOP exit() co nemá COMMIT bude ROLLBACK CONTINUE pokud lze, tak pokračovat DO <funkce> vykonání funkce <funkce> GOTO <návěstí> skok na návěstí <návěstí>
Hostitelské proměnné Výstupní Data: DB -> Program Vstupní INPUT část SELECT & FETCH Data: Program -> DB VALUES část INSERT SET část UPDATE WHERE, HAVING, FOR Kdekoli místo výrazu nebo hodnoty
Hostitelské proměnné Deklarované v programovacím jazyku i SQL EXEC SQL BEGIN DECLARE SECTION; int vara(15); short inda; VARCHAR varb; EXEC SQL END DECLARE SECTION;
Hostitelské proměnné hodnota nebo výraz Vstupní proměnná - konstanta řešení dynamické SQL EXEC SQL SELECT cola, colb INTO :vara, :varb FROM tbl ORDER BY :ignored; EXEC SQL DROP TABLE :ignored;
Hostitelské proměnné Vstupní hostitelská proměnná má být přiřazená int vara(15); VARCHAR varb[5]; convert(varb,"ahoj"); EXEC SQL INSERT INTO tbl (cola, colb) VALUES (:vara, :varb);
Indikační proměnné 2B integer Indikují hostitelské proměnné :hostitelska:indikacni Nejvíc 1 indikační na 1 hostitelskou Indikace výstupní hostitelské proměnné NULL, platná, zkrácená Indikace vstupní hostitelské proměnné NULL, platná
Indikační proměnné Indikace výstupní hostitelské proměnné (-1) reprezentuje NULL Hodnota hostitelské proměnné nedeterminována (0) reprezentuje platnou hodnotu (>0) délka před zkrácením SQLCA.SQLCODE == 0 (-2) zkrácená (neznámá původní délka)
Indikační proměnné Zpracování výstupní hostitelské proměnné EXEC SQL SELECT cola INTO :vara:inda FROM tbl WHERE ID=1; if (inda == 0) std::cout<<vara; else if (inda > 0) std::cout<<"truncated: "<<inda; else if (inda == -1) std::cout<<"null"; else if (inda==-2) std::cout<<"truncated unknown";
Indikační proměnné Vstupní hostitelská proměnná (-1) reprezentuje NULL (>0) reprezentuje platnou hodnotu
Indikační proměnné Zápis NULL hodnoty do sloupce colb int vara(15); short indb(-1); VARCHAR varb[6]; convert(varb, "dummy"); EXEC SQL INSERT INTO tbl (cola, colb) VALUES (:vara, :varb:indb);
Indikační proměnné Výběr řádků s možným NULL sloupcem colb int vara(15); short indb(-1); VARCHAR varb[6]; int varc(8); short indc(-1); EXEC SQL SELECT FROM tbl (cola, colb) INTO (:vara, :varb:indb) WHERE (colc = :varc) OR (colc IS NULL AND :varc INDICATOR :indc IS NULL);
Základní SQL příkazy Umožnují práci s daty, tabulkami, objekty Ověření úspěšnosti příkazu INSERT, UPDATE, DELETE Implicitně WHENEVER Explicitně SQLCA Typ výsledku SELECT žádný řádek / 1 řádek / více řádků
Základní SQL příkazy SELECT zisk dat z DB INSERT vložení dat do DB UPDATE změna dat v DB DELETE odstranění dat z DB
Základní SQL příkazy SELECT (jeden řádek) Nedeterminováno pro víceřádkový výsledek SELECT_ERROR (defaultně YES) Zisk dat z DB EXEC SQL SELECT cola, colb + 4 INTO :vara, :varb FROM tbl WHERE colc > :varc;
Základní SQL příkazy SELECT (jeden řádek) INTO výstupní hostitelské prom. FROM zdrojová tabulka WHERE podmínka CONNECT BY hierarchická data START WITH hierarchická data GROUP BY agregace HAVING podmínka agregace ORDER BY třídení FOR UPDATE OF optimalizace výsledku pro update
Základní SQL příkazy INSERT vložení dat do DB EXEC SQL INSERT INTO tbl (cola, colb, colc, cold) VALUES (:vara, :varb, varc:indc, NULL)
Základní SQL příkazy INSERT VALUES může obsahovat konstanty hostitelské proměnné funkce USER, SYSDATE
Základní SQL příkazy INSERT (s poddotazem) Vložení i více řádků EXEC SQL INSERT INTO tbl (cola, colb) SELECT cola, colb FROM cptbl WHERE colc > :varc;
Základní SQL příkazy UPDATE Změna dat v DB EXEC SQL UPDATE tbl SET cola=:vara, colb=:varb WHERE colc=:varc; EXEC SQL UPDATE tbl SET cola=:vara, colb= (SELECT MAX(colB)*2 FROM tbl2 WHERE colc=:varc) WHERE cold=:vard;
Základní SQL příkazy DELETE Smaže data z DB EXEC SQL DELETE FROM tbl WHERE cola=:vara;
Základní SQL příkazy WHERE Omezuje zpracovávanou množinu dat SELECT, UPDATE, DELETE Může obsahovat boolean podmínky skaláry hostitelská pole (kromě SELECT) poddotaz funkci Pokud chybí zpracována celá množna UPDATE, DELETE nastaví warning
DML Returning Clause Vrací změněný záznam INSERT, UPDATE, DELETE Pouze jeden záznam... RETURN cola, colb+3 INTO :vara:inda, :varb:indb
Kurzory Zpracování víceřádkové odpovědi Alternativa: hostitelská pole Práce s kurzory DECLARE pojmenování kurzoru a přiřazení k dotazu OPEN vykonání dotazu FETCH posun kurzoru a zisk jednoho řádku CLOSE deaktivace kurzoru
Kurzory DECLARE Deklarace kurzoru a přiřazení k dotazu dotaz se nevykonává unikátní názvy (prekompilovaná jednotka) signifikantních 31 (18 ANSI) znaků neobsahuje "_" Dotaz neobsahuje INTO část EXEC SQL DECLARE mycur CURSOR FOR SELECT cola, colb FROM tbl WHERE colc=:varc;
Kurzory OPEN Vykonání dotazu přiřazeného kurzoru rows-processed v SQLCA.SQLERRS nastaven na 0 pozice kurzoru před první výsledek Použít CLOSE před opětovným OPEN lze obejít MODE=ORACLE MAXOPENCURSORS lze nastavit při předpokladu použití hodně kurzorů EXEC SQL OPEN mycur;
Kurzory FETCH Posun ukazatele kurzoru a zisk řádku dotaz se nevykonává musí být otevřený "no data found code" SQLCA.sqlcode, SQLCODE, SQLSTATE test "WHENEVER NOT FOUND"
Kurzory FETCH EXEC SQL FETCH mycur INTO :varfirsta, varfirstb; EXEC SQL WHENEVER NOT FOUND DO BREAK; for(;;) { } EXEC SQL FETCH mycur INTO :vara, varb; process(vara,varb);
Kurzory CLOSE uzavře kurzor a uvolní prostředky EXEC SQL CLOSE mycur;
Kurzory CLOSE_ON_COMIT CLOSE_ON_COMMIT=YES zavře kurzory při ROLLBACK, COMMIT defaultní hodnota CLOSE_ON_COMMIT=no vyšší výkon, nezavírá se MODE=ORACLE zavře kurzor v CURRENT OF MODE=ANSI zavře všechny explicitní kurzory
Kurzory PREFETCH sníží počet přenosů DB<->aplikace defaultně 1 rozsah od 0 do 65535 neaktivní při výskytu některých sloupců LONG, LOB lze použít globálně, i pro jednotlivé kurzory
Kurzory CURRENT OF Kurzor lze použít také pro modifikaci poslední řádek získán FETCH kurzor musí být otevřený nesmí být proveden ROLLBACK, COMMIT kvůli zámkům EXEC SQL UPDATE tbl SET cola=:vara:inda WHERE CURRENT OF mycur;
Skrolovatelné kurzory Jako kurzor + nesekvenční přístup Práce se skrolovatelnými kurzory DECLARE SCROLL CURSOR jako DECLARE pro kurzor OPEN jako OPEN pro kurzor FETCH přístup k datům CLOSE jako CLOSE pro kurzor
Skrolovatelné kurzory FETCH FETCH FIRST první řadek výsledku FETCH PRIOR předchozí řádek výsledku FETCH NEXT následující řádek výsledku (jako FETCH pro kurzor) FETCH LAST poslední řádek výsledku FETCH CURRENT aktuální řádek výsledku FETCH RELATIVE n n-tý řádek výsledku relativně FETCH ABSOLUTE n n-tý řádek výsledku absolutně
Skrolovatelné kurzory FETCH EXEC SQL DECLARE mycur SCROLL CURSOR FOR SELECT cola FROM tbl; EXEC SQL OPEN mycur; EXEC SQL FETCH LAST mycur INTO :varlasta; EXEC SQL FETCH FIRST mycur INTO :varfirsta; EXEC SQL FETCH RELATIVE 8 mycur INTO :varra; EXEC SQL FETCH ABSOLUTE 18 mycur INTO :varaa; EXEC SQL FETCH NEXT mycur INTO :varna; EXEC SQL FETCH CURRENT mycur INTO :varca; EXEC SQL CLOSE mycur;
Dynamické SQL Dotazy vygenerované při runtime + Proměnlivé počty / typy sloupců / tabulek Náchylné na SQL injekce Nelze optimalizovat za překladu Méně přehledný kód
Dynamické SQL EXECUTE IMMEDIATE vhodné pro jednorázové dotazy PREPARE & EXECUTE vhodné pro opakované dotazy SQLDA pro neznámý počet / typ parametrů / sloupců
EXECUTE IMMEDIATE Dotaz z proměnné nebo řetězce char buffer[100]; convert(buffer,"update tbl SET cola=5"); EXEC SQL EXECUTE IMMEDIATE :buffer; EXEC SQL EXECUTE IMMEDIATE "UPDATE tbl SET cola=5";
EXECUTE IMMEDIATE Vykonání příkazu zahrnuje Parsování Optimalizaci Vytvoření plánu vykonání Provedení dotazu
PREAPARE & EXECUTE PREPARE Předpřipravení dotazu (pouze jednou) z proměnné z řetezce char buffer[100]; sprint(buffer,"update tbl SET cola=5"); EXEC SQL PREPARE qra FROM :buffer; EXEC SQL PREPARE qrb FROM "UPDATE tbl SET cola=5";
PREAPARE & EXECUTE PREPARE Předpřipravení dotazu zahrnuje Parsování Optimalizaci Vytvoření plánu vykonání Lze použít hostitelské proměnné EXEC SQL PREPARE qrparam FROM "UPDATE tbl SET cola=:vara:inda, colb=:varb"
PREAPARE & EXECUTE EXECUTE Vykonání předpřipraveného dotazu bez hostitelských proměnných s hostitelskými proměnnými rozhodující pořadí, nikoli název EXEC SQL EXECUTE qra; EXEC SQL EXECUTE qrparam USING :vara:inda, :varb;
PREAPARE & EXECUTE KURZORY Vykonání předpřipraveného dotazu s kurzorem EXEC SQL PREPARE qrsel FROM "SELECT cola FROM tbl WHERE colb=:varb:indb" EXEC SQL DECLARE cur FOR qrsel; EXEC SQL OPEN cur USING :varb:indb; EXEC SQL WHENEVER NOT FOUND DO BREAK; for(;;) { EXEC SQL FETCH cur INTO :vara; process(vara);} EXEC SQL CLOSE cur;
SQLDA SQL Descriptor Area Řeší neznámý počet a typ sloupců Inicializace DESCRIBE BIND VARIABLES vstupní hostitelské proměnné DESCRIBE SELECT LIST výstupní hostitelské proměnné #include<sqlda.h> SQLSLQDAAlloc();
SQLDA N (long) V (char**) L (long*) T (short*) I (short**) F (long) S (char**) M (short*) C (short*) X (char**) Y (short*) Z (short*) SQLDA struktura SQLDA
SQLDA struktura SQLDA N nejvyšší počet možných proměnných nastaveno při SQLSQLDAAlloc() po DESCRIBE aktuální počet proměnných F aktuální počet proměnných (po DESCRIBE) V ukazatel na pole adres hostitelských proměnných vstupní proměnné musí být nastaveny před OPEN výstupní proměnné se nastaví automaticky SQLSQLDAAlloc() alokuje pouze pole pro pointery
SQLDA struktura SQLDA L pointer na pole délek hostitelských prom. vstupní prom. musí být nastaveny před OPEN výstupní prom. maxlength při DESCRIBE lze upravit dle potřeby SQLNumberPrecV6() pro přesnost číselných typů T ukazatel na pole kódu typů interní / externí (automatická konverze) musí být nastaveno před OPEN
SQLDA externí datové typy SQL CODE Internal VARCHAR2 1 char[n] NUMBER 2 char[n] INTEGER 3 int FLOAT 4 float STRING 5 char[n+1] CHAR 96 char[n]
I S M SQLDA struktura SQLDA pointer na pole adres indikátorových prom. vstupní musí být nastaveny před OPEN pointer na pole adres názvů prom. nastaveno při DESCRIBE pointer na pole max délek názvů prom.
C SQLDA struktura SQLDA pointer na pole aktuálních délek názvů prom. nastaveno při DESCRIBE XYZ jako SMC pro indikátorové prom.
SQLDA DESCRIBE Vyplní SQLDA potřebnými daty názvy datové typy délky
SQLDA DESCRIBE SELECT LIST Deskriptor obsahuje Počet nalezených sloupců Adresy bufferů pro uložení výsledku Adresy indikátorových proměnných Kódy typů sloupců Pole názvů sloupců EXEC SQL DESCRIBE SELECT LIST FOR query INTO select_des;
SQLDA DESCRIBE BIND VARIABLES Deskriptor obsahuje Počet nalezených proměnných při DESCRIBE Adresy výstupních a indikátorových proměnných Kódy typů proměnných Jména výstupních proměnných EXEC SQL DESCRIBE BIND VARIABLES FOR query INTO bind_des;
SQLDA celý příklad EXEC SQL PREPARE query FROM :buffer; EXEC SQL DECLARE cur CURSOR FOR query; EXEC SQL DESCRIBE BIND VARIABLES FOR query INTO bind_des; EXEC SQL OPEN cur USING DESCRIPTOR bind_des; EXEC SQL DESCRIBE SELECT LIST FOR query INTO select_des; for (;;) { } EXEC SQL FETCH cur USING DESCRIPTOR select_des; process(); EXEC SQL CLOSE cur;
Zkratky prom. proměnná
Zdroje Pro*C/C++ Precompiler Programmer's Guide (6) Embedded SQL (13) Oracle Dynamic SQL Application Programming III (15) Dynamic SQL Application Programming I (13) Embedded SQL