Rajmund
Radziewicz
Oracle Pro*C - czyli zagnieżdżanie instrukcji SQL w C |
 |
W większości systemów bazodanowych, takich jak Oracle, DB2 czy
PostgreSQL istnieją narzędzia do tzw. wbudowanego SQL-a. Pozwalają one
łączyć instrukcje SQL z językami takimi jak C, C++, czy też Fortran.
Połączenie tego typu jest o tyle korzystne, że często daje większe
możliwości niż stosowanie języków proceduralnych, do jakich
m.in. należy PL/SQL. Języki proceduralne są ściśle zintegrowane z
serwerem baz danych, przez co nie mają wpływu na zewnętrzne obszary
aplikacji (chociażby warstwę kliencką).
Narzędzie dołączone do bazy Oracle pozwalające zagnieżdżać SQL-a w
kodzie C, nazywa się Pro*C. Jest to specjalny prekompilator -
który wszystkie osadzone w pliku instrukcje odwołujące się do
struktur bazy, zamienia na wywołania odpowiednich funkcji
bibliotecznych. Pisząc program dedykowany dla Pro*C nadajemy mu
rozszerzenie *.pc. Następnie przepuszczamy przez prekompilator:
W
wyniku prekompilacji otrzymamy właściwy plik z rozszerzeniem *.c, który
możemy skompilować za pomocą dowolnego kompilatora C-C++ (w Linuksie
np. GCC).
Instrukcje SQL w pliku *.pc powinny znaleźć się pomiędzy słowem kluczowym "EXEC SQL" a średnikiem:
void main()
{
int test;
EXEC SQL SELECT count(*) INTO :test FROM emp;
printf("Liczba rekordów: %d", test);
}
|
W
powyższym przykładzie deklarujemy zmienną "test" typu integer, a
następnie umieszczamy ją we frazie INTO. W analogiczny sposób
przekazuje się wynik zapytania z bazy do programu w języku PL/SQL.
Oczywiście
pojedyncze zmienne - to nie jedyne elementy jakie możemy przekazywać.
Mogą to być równie dobrze tablice, struktury, unie, czy też wskaźniki.
W języku C wskaźniki służą do wskazywania na inne zmienne lub pewien
obszar w pamięci komputera:
int *x;
EXEC SQL SELECT * INTO :x
FROM Tablica1
WHERE KOL1=200000;
|
W takiej sytuacji wynik wyrażenia SELECT odnosi się do wskaźnika *x - a nie konkretnej zmiennej x.
Ciekawą właściwością Pro*C jest także możliwość przekazywania w analogiczny sposób wskaźników do struktur. Np:
struct testowa_struktura {
long id_rekordu [SIZE];
char tytul [SIZE];
}
sql_wstaw ( struct testowa_struktura * opis, int liczb )
{
EXEC SQL FOR :liczb
INSERT INTO Opisy1
VALUES ( :opis );
} |
Funkcja
"sql_wstaw" - wstawia tutaj określoną przez "liczb" ilość opisów do
tablicy "Opisy1". Wskaźnik do struktury został przekazany jako parametr
tej funkcji. Zaletą takiego rozwiązania jest możliwość wykonywania
jednorazowo zbiorowych instrukcji INSERT. Słowo kluczowe FOR oznacza w
tym wypadku ilość opisów.
W Pro*C możemy także posługiwać się
kursorami. W celu wykonania instrukcji SQL - Oracle tworzy pewien
obszar roboczy nazywany przestrzenią kontekstu. W przestrzeni tej
przechowywane są informacje konieczne do wykonania tej instrukcji. Tak
jak w przypadku PL/SQL - Pro*C pozwala nazwać przestrzeń kontekstu i
odwoływać się do zawartych w niej danych za pomocą mechanizmu
nazywanego kursorem. Mamy możliwość posługiwania się dwoma typami
kursorów:
- jawnymi - gdzie użytkownik
może w sposób jawny utworzyć kursor dla zapytań, których wynikiem jest
wiele wierszy i wykonywać operacje na tych wierszach (najczęściej za
pomocą FOR);
- niejawnymi - tutaj Pro*C automatycznie tworzy kursor dla wszystkich wymaganych operacji.
Jeśli
wynik naszego zapytania zwraca dużą ilość rekordów, to możliwe jest
utworzenie kursora, który będzie umożliwiał dostęp do pojedynczych
wierszy ze zwracanej listy. Przykładowy szablon procedury
wykorzystującej kursor może wyglądać następująco::
int test;
EXEC SQL DECLARE nazwa_kursora CURSOR FOR SELECT ...
EXEC SQL OPEN nazwa_kursora;
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;) {
EXEC SQL
FETCH nazwa_kurosra INTO :test;
|
Istotne
jest również to - że możemy swobodnie łączyć programy *.pc z
procedurami i pakietami PL/SQL. Dla przykładu możemy otworzyć kursor
poprzez wywołanie istniejącej w bazie procedury PL/SQL'owej
open_emp_cur z poziomu programu Pro*C:
sql_cursor emp_cursor;
char emp_name[11];
...
EXEC SQL ALLOCATE :emp_cursor; /* alokacja zmiennych */
...
/* Otworzenie kursora */
EXEC SQL EXECUTE
begin
demo_cur_pkg.open_emp_cur(:emp_cursor, :dept_num);
end;
;
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
{
EXEC SQL FETCH :emp_cursor INTO :emp_name;
printf("%s\n", emp_name);
}
|
Podczas uruchamiania prekompilatora - możemy przekazywać mu szereg przydatnych parametrów. M.in.
- CLOSE_ON_COMMIT={YES | NO} - zamykanie wszystkich kursorów po wykonaniu commita
- DBMS={V7 | V8} - kompatybilność ze starszymi wersjami Oracle'a
- ERRORS={YES | NO} - wysyłanie komunikatów o błędach na terminal
- INCLUDE=ścieżka - zdefiniowanie ścieżki do plików dołączanych za pomocą dyrektywy # include
- MAXLITERAL=10..1024 - maksymalna długość stringów w wygenerowanym kodzie C
Na
zakończenie tego krótkiego wprowadzenia - warto może wspomnieć pewnych
o drobnych niedogodnościach związanych z używaniem omawianego
prekompilatora. Przede wszystkim brakuje w C funkcji do konwersji dat,
przez co zmuszeni jesteśmy używać typowo SQL-owych TO_CHAR/TO_DATE.
Brak '$' jako zamiennika EXEC SQL [[begin/end] declare section]
powoduje, że często małe programiki są sztucznie rozbudowane - i przez
to mniej czytelne.
powrót
|