Stáhnout prezentaci
Prezentace se nahrává, počkejte prosím
1
Zanořené SQL a dynamické SQL
Ondřej Štoček Petr Novák, Kateřina Zvánovcová
2
Historie Embedded SQL První implementace Embedded SQL byla použita v DB2 (IBM) v osmdesátých letech. Ta byla základem pro první ANSI SQL v roce 1986. Bohužel, ANSI SQL ‘86 a jeho oprava v roce 1989 definovaly jen podmnožinu Embedded SQL známý jako Static SQL.
3
Historie Emb. SQL – pokračování
Implementace kompletního Embedded SQL (s Dynamic SQL) v DB2 sloužila jako de facto standard do devadesátých let. Sloužila jako základ pro další databázové systémy (Oracle, FirstSQL…).
4
CÍL Propojení jazyka SQL s vyšším programovacím jazykem.
Přenositelnost zdrojového kódu databázové aplikace na různé databáze ... Přehlednost zdrojového kódu
5
Embedded SQL Místo volání patřičných funkcí příslušné databáze píšete SQL dotazy přímo do zdrojového kódu aplikace v jakémsi pseudojazyce. Před vlastní kompilací se dosadí na jejich místo nativní volání API daného SQL serveru.
6
Oracle preprocessor Spouští se ještě před vlastním překladem zdrojového kódu. Generuje z Embedded SQL datové struktury a série volání funkcí z knihovny Oracle SQLLIB. Pro* C/C++, Pro* Cobol, Pro* Fortran, … V dalším výkladu se zaměřím na Pro* C/C++
7
OCI (Oracle Call Interface) vs. Pro* C/C++
Menší velikost kódu Přímý přístup k funkcím, větší kontrola přístupu aplikace k databázi Poněkud vyšší výkon Pro* C/C++ Mnohem lepší čitelnost kódu Mnohem jednodušší konstrukce programu
8
Životní cyklus programu
Vytvoření programu s vloženým (embedded) SQL (soubor.pc) Prekompilace pomocí Pro* C/C++ (soubor.c) Kompilace překladačem C/C++ (soubor.obj) Přilinkování Oracle run-time knihovny a vytvoření spustitelného souboru
9
Syntax Embedded SQL Každý příkaz je uvozen: EXEC SQL
Zpracováván databázovým prekompilátorem. Příkaz končí středníkem (;). Komentáře se zapisují /* ... */. Komentáře uvedené //... jsou nepřípustné.
10
SQL a direktiva #include
Prekompilátor se spouští ještě před vlastním preprocesorem hostitelského jazyka Nutnost vlastní direktivy pro vložení souboru Obsahuje-li header "EXEC SQL" příkazy, inkludujeme pomocí: EXEC SQL INCLUDE file.h;
11
Hostitelské proměnné Určeny pro komunikaci databáze s prostředím programu. Použití proměnné v SQL uvozeno dvojtečkou, jinak je považována za databázové pole. :promenna Deklarace takových proměnných Začíná: EXEC SQL BEGIN DECLARE SECTION; Končí: EXEC SQL END DECLARE SECTION; Povolené typy: char, char[n], int, short, long, float, double, VARCHAR[n]
12
Indikátorové proměnné
jsou hostitelské proměnné (typu short) následující v SQL příkazu za jinou proměnnou. Může obsahovat následující hodnoty: 0: Proměnná koretně naplněna. -1: Vkládaná hodnota je NULL, proměnná má neurčitý obsah. -2: Vkládaná hodnota je větší než proměnná a není možné určit skutečnou velikost. >0: Vkládaná hodnota je větší než proměnná, skutečná velikost je hodnota indikátoru. (Při použití v INSERT / UPDATE jsou relevantní pouze hodnoty -1 a 0. )
13
Příklad použití hostitelských proměnných
EXEC SQL BEGIN DECLARE SECTION; char mujtext[20 + 1]; short ind_mujtext; EXEC SQL END DECLARE SECTION; ... EXEC SQL SELECT pole FROM tabulka INTO :mujtext :ind_mujtext WHERE druhepole = 15; if (ind_mujtext == -1) printf("NULL"); else printf(mujtext);
14
Připojení / odpojení databáze
EXEC SQL CONNECT :login/:passwd; EXEC SQL CONNECT :login IDENTIFIED BY :passwd; Odpojení: S uložením změn: EXEC SQL COMMIT WORK RELEASE; Bez uložení změn: EXEC SQL ROLLBACK WORK RELEASE;
15
Příklad použití CONNECT a INSERT
EXEC SQL BEGIN DECLARE SECTION; char user[20 + 1]; char passwd[20 + 1]; float insval[50]; EXEC SQL END DECLARE SECTION; strcpy(user, "novak"); strcpy(passwd, "123456"); EXEC SQL CONNECT :user IDENTIFIED BY :pwd; ... EXEC SQL INSERT INTO tabulka (mojepole) VALUES (:insval); EXEC SQL COMMIT WORK RELEASE;
16
Kurzory Prostředek pro zpracovávání víceřádkových výsledků SQL dotazů
Kurzor je pojmenovaný, můžeme mít několik různých kurzorů otevřených najednou.
17
Deklarace kurzoru Syntaxe:
EXEC SQL DECLARE jmeno_kurzoru CURSOR FOR SELECT...; jmeno_kurzoru: Jméno deklarovaného kurzoru. Jméno není uváděno s dvojtečkou. Nejedná se o hostitelskou proměnnou nýbrž o identifikátor pro SQL prekompilátor SELECT... : Dotaz, pro který se kurzor připraví
18
Otevření / zavření kurzoru
Syntaxe: EXEC SQL OPEN jmeno_kurzoru; ... EXEC SQL CLOSE jmeno_kurzoru;
19
Vyzvednutí záznamu a posun kurzoru
Syntaxe: EXEC SQL FETCH jmeno_kurzoru INTO :polozka1 :ind_prom1, :polozka2 :ind_prom2, ... ; jmeno_kurzoru: jméno otevřeného kurzoru polozka1,... : proměnné pro vyzvednutá data ind_prom1,...: indikátorová proměnná
20
Ošetření chyb, varování
Příkaz WHENEVER Nastaví od daného místa prekompilace způsob ošetřování varování, chyb a nepřítomnosti záznamu. Syntaxe: EXEC SQL WHENEVER podmínka akce
21
Ošetření chyb, varování
Podmínka: NOT FOUND: je-li odpověď na FETCH nebo SELECT žádná položka, je vygenerována akce. SQLERROR: skončí-li jakákoliv akce SQL EXEC chybou, je provedena akce. SQLWARNING: skončí-li jakákoliv akce SQL EXEC varováním, je provedena akce.
22
Ošetření chyb, varování
Řešení: CONTINUE: program pokračuje následujícím příkazem. GOTO label: program skočí na zadaný label. STOP: program se ukončí. DO routine: program zavolá funkci routine. DO BREAK: zavolá break pro opuštění cyklu. DO CONTINUE: zavolá continue pro pokračování dalšího kroku cyklu.
23
Příklad použití kurzoru a ošetření posledního řádku
EXEC SQL BEGIN DECLARE SECTION; int cislo; short i_cislo; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE mujkurzor CURSOR FOR SELECT pole FROM tabulka WHERE druhepole > 20; EXEC SQL OPEN mujkurzor; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (true) { EXEC SQL FETCH mujkurzor INTO :cislo :i_cislo; if (i_cislo == -1) printf("NULL\n"); else printf("Hodnota %i\n",cislo); } EXEC SQL CLOSE mujkurzor;
24
Dynamické SQL Co to je K čemu se to hodí Jak se to používá
Jak se zpracovává SQL dotaz Proč statické SQL K čemu se to hodí Stavění podmínek podle zadání DDL v procedurách Jak se to používá Několik různě náročných způsobů
25
Zpracování SQL dotazu Lexikální, syntaktická a sémantická analýza
SELECT jmeno, prijmeni FROM student st WHERE kruh_id = 13 AND (SELECT sum(castka) FROM platby pl WHERE pl.student_id = st.student_id AND mesic = CURRENT_MONTH) < 700 Lexikální, syntaktická a sémantická analýza Překlad do p-kódu p-kód – posloupnost operací, která bude databáze opravdu provádět (“v tabulce xxx najdi řádek podle indexu yyy”) Spuštění
26
Zpracování SQL dotazu Analýza dotazu může být dosti náročná záležitost
Možné optimalizace Cache I ta k rozumné funkci potřebuje používání proměnných Překlad předem Embedded SQL, procedury v databázi Dynamic SQL se sestavuje a spouští právě z tohoto předem přeloženého kódu
27
Omezení statického SQL
Daná povahou p-kódu Samotný dotaz musí být vždy stejný Nelze tvořit věci ve stylu mysql_query(“SELECT id, nazev FROM clanek WHERE $podminka”); Proměnné mohou být v dotazech používány jen v klauzulích WHERE a VALUES Nelze SELECT :sloupce FROM student WHERE student_id = 1; ani CREATE TABLE :jmeno_tabulky;
28
Použití dynamického SQL
Manipulace se strukturou databáze (DDL příkazy) Tvoření a úprava tabulek podle aktuální potřeby Sestavování složitějších podmínek podle vstupu Simulace SQL konzole ...
29
Nevýhody dynamického SQL
Pomalejší Pokaždé se musí předkládat znovu Může zavléci chyby SQL Injection
30
Volání dynamického SQL
V Embedded SQL jsou čtyři způsoby (metody) volání podle složitosti dotazu: Bez výstupu i proměnných Bez výstupu, s pevně daným počtem proměnných Se pevně daným počtem polí výstupu i vstupních proměnných S neznámým počtem polí výstupu a neznámým počtem proměnných
31
Metoda 1 Bez výstupu a bez proměnných Klauzule EXECUTE IMMEDIATE
V embedded SQL EXEC SQL EXECUTE IMMEDIATE ... V PL/SQL EXECUTE IMMEDIATE { :proměnná | 'řetězec' }; Spustí dotaz v parametru Dotaz se pokaždé překládá znovu
32
EXECUTE IMMEDIATE char prikaz[250], tab[200]; ... printf("Ktera tabulka se ti nelibi? "); gets(tab); sprintf(prikaz, “drop table %s”, tab); EXEC SQL EXECUTE IMMEDIATE :prikaz; ...
33
Metoda 2 Známý počet proměnných, výstup není možný Dvě fáze
PREPARE EXEC SQL PREPARE můj_dotaz FROM { :proměnná | 'řetězec' }; EXECUTE EXEC SQL EXECUTE můj_dotaz [ USING :proměnná:indikátor, ... ]; Jednou připravený dotaz je možno spouštět opakovaně
34
PREPARE/EXECUTE int sid; char vstup[150], dotaz[200]; printf(“Zadej podminku pro vyhozeni ”); gets(vstup); sprintf(dotaz, “delete from student where student_id = :id and %s”, vstup); EXEC SQL PREPARE muj_dotaz FROM :dotaz; for (;;) { printf(“ID? “); gets(vstup); sid = atoi(vstup); if (sid == 0) { break; } EXEC SQL EXECUTE muj_dotaz USING :sid; }
35
V PL/SQL V PL/SQL EXECUTE IMMEDIATE umí i proměnné
CREATE PROCEDURE vyhodit_studenta (podm VARCHAR, st_id NUMBER) AS BEGIN EXECUTE IMMEDIATE 'DELETE FROM student WHERE student_id = :id AND ' || podm USING st_id; END;
36
Odkazy Oracle Oracle Dynamic SQL, Pro*C/C++ Programmer's Guide
Performing SQL Operations with Native Dynamic SQL, PL/SQL User's Guide and Reference
37
Metoda 3 - úvod dynamické vytvoření dotazu vytvoření dotazu z řetězce
příkaz PREPARE práce s kurzorem tím se liší od předchozích DECLARE, OPEN, FETCH, CLOSE
38
Metoda 3 – typické použití
zadání podmínky ve WHERE klauzuli ne úplně obecně nutno dodržet pevný počet proměnných Příklad řetězce použitelného s metodou 3: "SELECT JmenoZam FROM Zam WHERE Plat > :mujplat"
39
Metoda 3 - příkazy EXEC SQL PREPARE jm_dotaz FROM
{ :host_retezec | retezec_literal }; EXEC SQL DECLARE jm_cursor CURSOR FOR jm_dotaz; EXEC SQL OPEN jm_cursor [USING host_var_sez]; EXEC SQL FETCH jm_cursor INTO host_var; EXEC SQL CLOSE jm_cursor;
40
PREPARE vytvoření dotazu z řetězce a jeho pojmenování jiná možnost
char dotaz[132] = "SELECT JmenoZam, CisloOdd FROM Zam WHERE Plat > :mujplat"; EXEC SQL PREPARE sql_dotaz FROM : dotaz; jiná možnost EXEC SQL PREPARE sql_dotaz FROM SELECT JmenoZam, CisloOdd FROM EMP WHERE Plat > :mujplat;
41
DECLARE deklaruje kurzor, pojmenuje ho asociuje ho s daným dotazem
EXEC SQL DECLARE zam_cursor CURSOR FOR sql_dotaz; zam_cursor není hostitelská proměnná je to SQL identifikátor přesto by měl být unikátní
42
OPEN výkonný příkaz alokuje kurzor váže hostitelské proměnné
“dosadí proměnné” provede dotaz vytvoří jeho aktivní množinu výsledky Př.: EXEC SQL OPEN zam_cursor USING :mujplat;
43
FETCH vrací řádku z aktivní množiny
dosadí vrácené hodnoty do daných hostitelských proměnných posune kurzor na další řádku nejsou-li další data - “no data found” nastaví chybový kód v sqlca.sqlcode Př.: EXEC SQL FETCH zam_cursor INTO :jmeno, :oddeleni;
44
CLOSE zavření kurzoru už se na něj nedá provést FETCH Př.:
EXEC SQL CLOSE zam_cursor;
45
Metoda 3 - rekapitulace char dotaz[132] =
"SELECT JmenoZam, CisloOdd FROM Zam WHERE Plat > :mujplat"; EXEC SQL PREPARE sql_dotaz FROM :dotaz; EXEC SQL DECLARE zam_cursor CURSOR FOR sql_dotaz; EXEC SQL OPEN zam_cursor USING :mujplat; EXEC SQL FETCH zam_cursor INTO :jmeno :oddeleni; EXEC SQL CLOSE zam_cursor;
46
Metoda 4 - úvod umožňuje zadat předem neznámý počet proměnných v příkazu umožňuje předem neznámý počet vybíraných položek (sloupců) v dotazu Př.: 'INSERT INTO Zam (<unknown>) VALUES (<unknown>)' 'SELECT <unknown> FROM Zam WHERE CisloOdd = 20‚ na tohle metoda 3 nestačí
47
Deskriptory dodatečná informace bind deskriptor select deskriptor
počet proměnných a návratových hodnot jejich délka, typ adresy vazebních proměnných adresy výstupních proměnných na návratové hodnoty tyto údaje potřebujeme znát, ale metoda 4 je předem neurčuje bind deskriptor pro informace o vazebních proměnných select deskriptor pro informace o výstupních položkách výsledcích SELECTu
48
struktura SQLDA SQL Description Area struktura pro deskriptory
deklarována v hlavičkovém souboru sqlda.h Př.: # include <sqlda.h> ... SQLDA * bind_descriptor; SQLDA * select_descriptor;
49
Položky SQLDA long N; /* počet položek deskriptoru */
char **V; /* ukazatel na pole adres proměnných */ struct SQLDA { long *L; /* ukazatel na pole délek bufferů */ short *T; /* ukazatel na pole typů bufferů */ short **I; /* ukaz. na pole adr. indikátorových p.*/ long F; /* počet prom. nalez. přík. DESCRIBE */ char **S; /* ukazatel na pole ukaz. na jm. prom.*/ short *M; /* ukaz. na pole max. délek jm. prom.*/ short *C; /* uk. na pole skutečných délek jm. pr.*/ char **X; /* ukaz. na pole jmen indikátorových pr */ short *Y; /*ukaz. na pole max. délek jmen ind. pr.*/ short *Z; /* uk. na pole skutečných délek jmen indikátorových poměnných */ };
50
Metoda 4 - kroky deklarování hostitelské proměnné pro text příkazu (string) deklarování struktur SQLDA pro select deskriptor a bind deskriptor alokování struktur SQLDA nastavení max. počtu návratových hodnot a proměnných v dotazu naplnění hostitelské proměnné textem SQL příkazu vytvoření dotazu z hostitelského řetězce PREPARE deklarování kurzoru pro dotaz DECLARE
51
Metoda 4 - kroky popsání vazebních proměnných
DESCRIBE BIND VARIABLES FOR sql_dotaz INTO bind_des; nastavení počtu proměnných v dotazu na počet skutečně nalezený příkazem DESCRIBE získání hodnot a alokace místa pro vazební proměnné nalezené DESCRIBE otevření kurzoru (za použití bind deskriptoru) popsání výstupních hodnot DESCRIBE SELECT LIST FOR sql_prikaz INTO select_des;
52
Metoda 4 - kroky nastavení skutečného počtu výstupních hodnot (DESCRIBE). nastavení skutečných délek a datových typů výstupních hodnot provedení FETCH s použitím alokovaných bufferů na které ukazuje select descriptor zpracování vrácených hodnot dealokace prostoru pro návratové hodnoty, proměnné, indikátorové proměnné, deskriptory zavření kurzoru (CLOSE)
53
Možné zjednodušení pokud je počet a typ proměnných předem známý, nepoužijeme bind deskriptor a příslušnou část vyřešíme jako u metody 3 pokud je předem známý počet a typ výstupních hodnot, nepoužijeme select deskriptor příslušnou část řešíme jako u metody 3
54
Metoda 4 – příkazy 1. PREPARE, DECLARE stejně jako metoda 3:
EXEC SQL PREPARE jm_prikazu FROM { :host_retezec | retezec_literal }; EXEC SQL DECLARE cursor_jm CURSOR FOR jm_prikazu; na konci CLOSE jako u metody 3 EXEC SQL CLOSE cursor_jm;
55
Metoda 4 – příkazy 2. EXEC SQL DESCRIBE BIND VARIABLES
FOR jm_prikazu INTO jm_bind_descr; EXEC SQL OPEN jm_cursor [USING DESCRIPTOR jm_bind_descr]; EXEC SQL DESCRIBE [SELECT LIST FOR] jm_prikazu INTO jm_select_descr; EXEC SQL FETCH jm_cursor USING DESCRIPTOR jm_select_descr;
56
Závěr Metoda 4 poskytuje nejvíce flexibility, ale její použití je velmi pracné větší náchylnost k chybám zásada: vždy použít to nejjednodušší, co stačí existuje několik variant Oracle Dynamic SQL vs. ANSI Dynamic SQL ANSI – novější, větší možnosti podpora objektových typů, UNICODE, ....
57
Odkazy www.oracle.com Pro* C/++ Precompiler Programmers Guide
a další dokumentace
Podobné prezentace
© 2024 SlidePlayer.cz Inc.
All rights reserved.