Josef Troch (troch@mail.ru)
23.10.2002 (poslední změna 4.11.2002)
Embedded SQL v Oraclu - Pro*C/C++
Pozn.: Všechny níže uvedené informace by se měly vztahovat k Oraclu 8.1.5
Embedded SQL
Embedded SQL ("vložené SQL") je jednou z metod, jak přistupovat k databázi z
prostředí nějakého procedurálního programovacího jazyka. Jak název napovídá, jde o vkládání SQL
příkazů mezi příkazy programovacího jazyka.
Embedded SQL je obsaženo ve většině významnějších databázových produktů včetně Oraclu.
Oracle nabízí podporu pro několik vyšších programovacích jazyků - nás bude zajímat podpora pro
jazyky C a C++. Tu zajišťuje prekompilálor Pro*C/C++.
Postup při tvorbě programu
- Vytvoření programu v C / C++ obsahujícího embedded SQL (dle konvence by se měl jmenovat
filename.pc)
- Prekompilace programu pomocí Pro*C/C++ (vznikne nám soubor filename.c)
- Kompilace programu (standartním C či C++ kompilátorem)
- Slinkování s Oracle run-time knihovnou
Prakticky:
Budeme se zabývat spouštěním prekompilátoru atd. na naší unixové instalaci.
Pozn.: Za momentálního stavu je třeba přenastavit proměnnou ORACLE_HOME na ..../8.1.5
a adekvátně upravit i LD_LIBRARY_PATH.
- Prekompilace: proc INAME=filename.pc
- Kompilace: gcc -c -I $ORACLE_HOME/precomp/public/ filename.c
- Slinkování: je složitější - je třeba přilinkovat řadu knihoven, proto je asi nejjednodušší
použít předpřipravený skript $ORACLE_HOME/bin/pcc (z webu stáhnutelná kopie
zde). Skript zajišťuje všechny 3 fáze - tedy prekompilaci,
kompilaci i linkování - na vstupu očekává jméno souboru (příponu .pc si doplní sám), vytvoří
stejnojmenný (bez přípony) spustitelný soubor. Jednoduchou úpravou získáte skript, který
bude zvládat i více vstupních souborů. Autorem skriptu je
Mgr M. Kopecký.
Příklad jednoduchého programu v Embedded SQL
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlcpr.h>
#include <sqlca.h>
EXEC SQL BEGIN DECLARE SECTION;
/* Hostitelske promenne: */
VARCHAR username[20];
VARCHAR password[20];
VARCHAR name[20];
VARCHAR job[20];
/* Indikatorove promenne: */
short nameIndicator;
EXEC SQL END DECLARE SECTION;
int main()
{
strncpy((char *) username.arr, "user", 20);
username.len = (unsigned short) strlen((char *) username.arr);
strncpy((char *) password.arr, "password", 20);
password.len = (unsigned short) strlen((char *) password.arr);
EXEC SQL CONNECT :username IDENTIFIED BY :password;
EXEC SQL SELECT ename, job
INTO :name:nameIndicator, :job
FROM EMP WHERE EMPNO = '7934';
printf("Name: Job:\n");
if (nameIndicator == -1) printf("NULL");
else printf("%s", name.arr);
printf(" %s", job.arr);
EXEC SQL ROLLBACK WORK RELEASE;
return 0;
}
Výše uvedený příklad najdete přímo připravený v souboru zde.
Příklady (samozřejmě výrazně složitější) dodávané s Oraclem v adresáři
$ORACLE_HOME/PRECOMP/DEMO/PROC/
Syntaxe Embedded SQL
V programu je možno libovolně prokládat kompletní SQL výrazy a kompletní C výrazy a používat
C-proměnné či struktury v SQL výrazech. Často jediným požadavkem na vkládané SQL výrazy je to, že
musí začínat EXEC SQL a končit středníkem.
Prekompilátor překládá všechna EXEC SQL makra na volání runtimové knihovny SQLLIBS. Jak
vypadá kód jednoduchého PL/SQL příkazu po prekompilaci si můžete prohlédnout
zde (či
zde v lokální kopii).
PL/SQL bloky - prekompilátor se k PL/SQL bloku chová jako kdyby byl jedním Embedded SQL
výrazem. PL/SQL blok se zde uzavírá mezi výrazy EXEC SQL EXECUTE a END-EXEC.
Komentáře - v Embedded SQL lze používat stejné typy komentářů jako v PL-SQL, tedy:
- jednořádkové komentáře typu:
-- Toto je komentar.
- a (potencielně) víceřádkové komentáře typu:
/* Toto je taky komentar. .... */
Problém nastává u jednořádkových komentářů - použijete-li výše uvedenou syntaxi, při prekompilaci
nebudou vadit, ale prekompilátor je v kódu (alespoň v některých případech - např. řádek začínající
--) nechá, což způsobí chybu při vlastní kompilaci. Oproti tomu třeba v sekci deklarací
nelze použít ani C++ jednořádkových komentářů - kvůli prekompilátoru. Proto je nejbezpečnější
všude používat (potencielně) víceřádkové komentáře - ty jsou stejné v C/C++ i v Embedded SQL.
Hostitelské a indikátorové proměnné
Hostitelské proměnné (host variables) slouží ke komunikaci Oraclu s naším programem - jsou
to jednoduché či složené deklarované v C a sdílené s Oraclem (tedy náš program i Oracle k nim mohou
přistupovat). Dle způsobu použití (vzhledem k Oraclu) je lze rozdělit na vstupní a výstupní.
V SQL výrazech musí být hostitelské proměnné prefixovány dvojtečkou (př.: EXEC SQL SELECT name
INTO :mojePromenna FROM ....;)
Několik hostitelských proměnných můžeme sdružit do C struktury (struct). Když pak použijeme jméno
struktury v Embedded SQL výrazu (zase prefixované dvojtečkou), Oracle použije každý z prvků
struktury jako hostitelskou proměnnou.
Hostitelskou proměnnou nelze použít pro zastoupení nějakého SQL klíčového slova nebo jména
Oraclovského objektu - potřebujeme-li něco takového, lze to řešit pomocí dynamického Embedded SQL.
Indikátorové proměnné - libovolnou hostitelskou proměnnou lze v Embedded SQL výrazu svázat s
indikátorovou proměnnou. Indikátorová proměnná je typu short a indikuje stav či hodnotu
hostitelské proměnné. Indikátorovou proměnnou lze použít k přiřazení NULL hodnoty do Oraclu
(samotnou hostitelskou proměěnou to nejde) nebo k detekci NULL hodnot či oříznutých hodnot ve
výstupních hostitelských proměnných.
V SQL výrazech musí být jméno indikátorové proměnné uvozeno dvojtečkou a okamžitě následovat za
jménem hostitelské proměnné (resp. mezi tyto dvě proměnné lze vložit klíčové slovo
INDICATOR - zbytečné).
Obdobně jako hostitelské proměnné i indikátorové proměnné lze sdružovat do struktur - v SQL výrazu
pak píšeme
... :strukturaHostitelskychPromennych:strukturaIndikatorovych promennych ...
Samozřejmě, obě struktury zde musí mít stejně prvků - a tyto si odpovídají pořadím.
Pozn.: Pro vstupní hostitelskou proměnnou, má-li indikační proměnná hodnotu -1, dosadí Oracle do
příslušného sloupce NULL hodnotu a ignoruje hodnotu v hostitelské proměnné. Má-li indikátorová
proměnná nezápornou hodnotu, Oracle zapíše do sloupce hodnotu hostitelské proměnné. Opačně u
výstupní host. proměnné: indikační hodnota 0 = hodnota v host. proměnné je v pořádku; -1 = hodnata
sloupce je NULL, hodnota host. proměnné neurčena; >0 = v hostitelské proměnné se vrací ořezaná
hodnota, indikační proměnná udává původní délku hodnoty ve sloupci; -2 = jako předchozí, původní
délku nelze určit.
Pozn.: Hostitelské proměnné jsou externích datových typů, při práci s Oraclem dochází ke konverzím
na interní datové typy a opačně.
Sekce deklarací
Všechny hostitelské (a snad i indikátorové) proměnné je třeba uvést v sekci deklarací (declare
section). Takovýchto sekcí může být v programu více a mohou se vyskytovat všude, kde se v
hostitelském programovacím jazyce mohou objevit deklarace proměnných.
Syntaxe je následující:
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR name[20];
...
...
EXEC SQL END DECLARE SECTION;
Řetězcové typy
V Embedded SQL lze jako vstupní i výstupní řetězcovou proměnné používat proměnné typu
char *. Zatímco jejich použití pro vstupní (hostitelské) proměnné je bezproblémové
(tedy za předpokladu, že nepředáváte C-čkový NULL), u výstupních vznikají potíže - Oracle
netuší, kolik prostoru pro danou řetězcovou proměnnou má k dispozici, a tedy kolik do ní může
naplnit byte-ů. Řeší to tedy tak, že na danou proměnnou spustí strlen() - a dle jeho
výsledku se zařídí.
Pokud tedy chcete používat char * i pro výstupní hostitelské proměnné, nezbývá vám, než
(typicky) před každým použitím proměnnou naplnit tak, aby strlen vrátil "správnou"
hodnotu.
Jako rozumnější se tedy jeví používat "Oraclovský" typ VARCHAR - jde o
strukturu obsahující C-čkové pole a prvek udávající platnou délku. Její použití je vidět ve výše
uvedeném příkladě.
Připojení se k databázi
Abychom mohli v programu s databází pracovat, musíme se k ní připojit. K tomu nám poslouží příkaz
CONNECT, jehož zjednodušená syntaxe je:
EXEC SQL CONNECT :user IDENTIFIED BY :password;
Proměnné user a password by měly být dle Oraclu typu char (asi spíše char[]) či
VARCHAR. Lze použít rovněž syntaxi:
EXEC SQL CONNECT :usr_pwd; , kde usr_pwd obsahuje username a password oddělené lomítkem.
Příklady (statického) Embedded SQL
- EXEC SQL INSERT INTO ctenari (id, jmeno, prijmeni) VALUES (:myId, :myJmeno, :myPrijmeni);
- EXEC SQL SELECT jmeno, prijmeni
INTO :myJmeno:myJmenoIndikator, :myPrijmeni:myPrijmeniIndikator FROM ctenari WHERE
id=:myId;
Pozn.: Předpokládáme-li, že myJmeno a myPrijmeni jsou jednoduchého
typu, musí tento SELECT vracet pouze jednu hodnotu. Může-li tento vracet více hodnot, je
třeba použít kursor (viz níže) nebo pole, do kterého nám Oracle nasype hodnoty
odpovídající jednotlivým řádkům.
-
EXEC SQL DECLARE CURSOR cursorCtenari FOR
SELECT jmeno, prijmeni FROM cursorCtenari WHERE id = :myId;
EXEC SQL OPEN cursorCtenari;
for(;;)
{
EXEC SQL FETCH cursorCtenari INTO :myJmeno,:myPrijmeni
if (sqlca.sqlcode != 0) break;
}
EXEC SQL CLOSE cursorCtenari;
Příklad použití kursoru. Struktura sqlca slouží k obsluze běhových chyb, je třeba includovat
header sqlca.h
Zdroje a odkazy:
Tento článek smí být používán libovolně, za předpokladu, že nebude modifikován a že
takto bude učiněno se zmínkou o autorovi a odkazem na stránku
http://jt.sf.cz, odkud by měla být dostupná aktuální verse
tohoto článku.
Snažím se, aby informace zde uvedené byly pravdivé a pokud možno přesné, nicméně nenesu žádnou
odpovědnost za to, že tomu tak opravdu je, ani za jakékoli škody, které by někomu v důsledku
případných špatných či nepřesných informací z tohoto článku mohly vzniknout.
Jakékoliv dotazy či připomínky mi můžete poslat mailem.
Zpět na stránku referátů a jiných výtvorů