Embedded SQL jest to technika bezpośredniego wpisywania poleceń SQL jako instrukcji języka zanurzającego, w tym przypadku języka C. W PostgreSQL służy do tego narzędzie o nazwie ECPG.
Program w C z wbudowanymi poleceniami SQL jest najpierw przetwarzany preprocesorem ecpg na program w języku C. Preprocesor rozpoznaje polecenia SQL zawarte w programie i zastępuje je wywołaniami funkcji z biblioteki CLI dla SQL. Otrzymany program przetwarza się normalnym kompilatorem C na program wykonywalny.
ecpg -I/usr/include/ecpg test1.pgc cc -I/usr/include/ecpg -o test1 test1.c -L/usr/local/lib -lecpg
Wszystkie polecenia SQL muszą być poprzedzane frazą EXEC SQL i kończyć się średnikiem (,,;''). Można je umieszczać gdziekolwiek w programie w C pilnując jedynie, aby deklaracje poprzedzały polecenia wykonywalne. Przykład:
int main () { EXEC SQL BEGIN DECLARE SECTION; int w; EXEC SQL END DECLARE SECTION; ... EXEC SQL SELECT wiek INTO :w FROM Zwierz WHERE imie='Kropka'; ... printf("Kropka waży %d kilo\n", w); ... }
Aby połączyć się z bazą danych należy użyć:
EXEC SQL CONNECT TO bd@rainbow USER scott/tiger;
zaś rozłączenia dokonujemy pisząc
EXEC SQL DISCONNECT;
Obsługę błędów najprościej robi się umieszczając na początku programu polecenie
EXEC SQL WHENEVER SQLERROR SQLPRINT;
(do konstrukcji WHENEVER jeszcze wrócimy).
W wyrażeniach SQL można umieszczać zmienne programu. Deklaruje się je wewnątrz sekcji DECLARE używając normalnej składni C.
EXEC SQL BEGIN DECLARE SECTION; deklaracje EXEC SQL END DECLARE SECTION;
Mozna używać następujących typów zmiennych:
char
char[n]
int
short
long
float
double
VARCHAR[n]
Typ VARCHAR służy do przechowywania napisów zmiennej długości (jak w SQL) i jest reprezentowany sw ECPG trukturą o dwóch polach. Pole len podaje aktualną długość napisu zapisanego w polu arr, będącym tablicą znakową o rozmiarze n. Uwaga: ciąg znaków w tej tablicy nie jest napisem w sensie C -- nie jest zakończony bajtem zerowym.
EXEC SQL BEGIN DECLARE SECTION; VARCHAR imie[40]; int waga; EXEC SQL END DECLARE SECTION;
Odwołanie do zmiennej w wyrażeniach SQL wymaga poprzedzenia jej nazwy dwukropkiem (,,:''). Gdy pełni ona rolę literału (np. w warunkach WHERE) nie należy jej otaczać apostrofami; takie rzeczy ECPG robi samodzielnie.
Ponieważ polecenie SELECT zwykle zwraca wiele wierszy, do ich przyjęcia należy używać kursorów. Kursor należy najpierw zadeklarować:
EXEC SQL DECLARE k1 CURSOR FOR SELECT imie, waga FROM Zwierz WHERE gatunek='lew';
a następnie otworzyć go
EXEC SQL OPEN k1;
Po otwarciu można z kursora pobierać kolejne wiersze do uprzednio zadeklarowanych zmiennych
EXEC SQL FETCH NEXT IN k1 INTO :imie, :waga; if (sqlca.sqlcode == 0) { imie.arr[imie.len - 1] = '\0'; printf("Imie: %s, waga: %d\n", imie.arr, waga); }
Po wyczerpaniu wierszy wyniku kursor należy zamknąć
EXEC SQL CLOSE k1;
Ponieważ w C nie istnieją dogodne wartości, którymi można by reprezentować NULL, trzeba w iiny sposób przekazać informacje o ich wystąpieniu w wyniku zapytania. Używa się do tego dodatkowych zmiennych informacyjnych typu całkowitego (indicator variables), po jednej dla każdej kolumny, w której może pojawić się wartość pusta. Taką zmienną umieszcza się we frazie INTO bezpośrednio po właściwej zmiennej, oddzielając ją od niej dwukropkiem.
int waga_p; ... EXEC SQL FETCH NEXT IN k1 INTO :imie, :waga:waga_p;
Jeśli wartością zmiennej informacyjnej nie jest zero, to wystąpiła wartość pusta (NULL).
Opisane powyżej mechanizmy wystarczą dla większości aplikacji, czasem jednak polecenie SQL musi być skponstruowane dopiero w trakcie wykonania programu. Służy do tego dynamiczny SQL, pozwalający budować polecenia w zmiennych napisowych, a następnie wykonywać je. Polecenie PREPARE zamienia napis na polecenie SQL, po czym poleceniem EXECUTE wykonujemy je.
char zapyt[256]; sprintf(zapyt, "SELECT waga FROM %s WHERE imie = 'Kropka'", tabela); EXEC SQL PREPARE zapytanie FROM :zapyt; EXEC SQL EXECUTE zapytanie INTO :w;
Polecenia PREPARE i EXECUTE można połączyć:
char *pol = "INSERT INTO Gatunki VALUES('krowa, 'Europa')"; EXEC SQL EXECUTE IMMEDIATE :pol;
Zamiast EXECUTE można użyć kursora:
char zapyt[256]; sprintf(zapyt, "SELECT waga FROM %s WHERE imie = 'Kropka'", tabela); EXEC SQL PREPARE zapytanie FROM :zapyt; EXEC SQL DECLARE k1 CURSOR FOR zapytanie;
Po wykonaniu każdego polecenia SQL informacja o jego wykonaniu jest zapisywana w strukturze SQLCA. Programista może bezpośrednio odczytać pola tej struktury lub użyć konstrukcji WHENEVER.
SQLCA (SQL Communications Area) to standardowa struktura do przekazywania informacji o przebiegu wykonania poleceń SQL, zwłaszcza o napotkanych błędach. Programy mogą odczytywać po każdym poleceniu zawartość jej pól.
Aby odwoływać sie do SQLCA należy dołączyć plik nagłówkowy sqlca.h poleceniem
EXEC SQL INCLUDE sqlca;
na początku programu.
Struktura ta ma następującą postać
struct sqlca { char sqlcaid[8]; long sqlabc; long sqlcode; struct { int sqlerrml; char sqlerrmc[70]; } sqlerrm; char sqlerrp[8]; long sqlerrd[6]; char sqlwarn[8]; char sqlstate[5]; };
Poszczególne pola mają następujące znaczenie:
sqlcaid --
inicjowane na "SQLCA"
, identyfikuje SQL Communications Area.
sqlcabc -- długość (w bajtach) struktury SQLCA.
sqlcode -- kod wykonania (status) ostatniego polecenia SQL, liczba całkowita:
|
sqlerrm -- struktura zawierająca komunikat o błędzie, dwie składowe:
sqlerrml -- długość komunikatu.
sqlerrmc -- tekst komunikatu (maks. 70 znaków) odpowiadającego numerowi błędu z sqlcode.
sqlerrp -- zarezerwowane
sqlerrd -- sześcioelementowa tablica liczb całkowitych, interesujące wartości:
sqlerrd[2] -- liczba przetworzonych wierszy dla ostatniego polecenia SQL.
sqlerrd[4] -- pozycja znaku w ostatnim poleceniu SQL, od której stwierdzono błąd skłądniowy.
sqlwarn --
Ośmioelementowa tablica znakowa zawierająca flagi ostrzeżeń, flagi
są ustawiane przez wpisanie znaku 'W'
.
|
sqlstate -- inny, zalecany w nowym standardzie sposób zapisywania kodu wykonania. "00000" oznacza poprawne wykonanie, każda inna wartość to ostrzeżenie lub błąd.
Służy do automatycznej obsługi błędów i wyjątków. Składnia:
EXEC SQL WHENEVER <warunek> <akcja>;
Obsługa następuje przez każdorazowe sprawdzanie struktury SQLCA i automatyczne wykonanie <akcji> po wykryciu <warunku>.
<Warunek> może mieć następującą postać:
SQLWARNING -- sqlwarn[0] jest ustawione, czyli ostrzeżenie;
SQLERROR -- sqlcode jest ujemne, czyli błąd;
NOT FOUND -- sqlcode wynosi 100, czyli nie znaleziono wierszy spełniających warunki podane w WHERE albo FETCH nie zwróciło żadnego wiersza.
<Akcja> może mieć postać:
CONTINUE -- kontynuacja od następnej instrukcji;
BREAK -- wyjście z bieżącej pętli;
DO -- wywołanie funkcji obsługi błędu;
STOP -- wycofanie transakcji i wyjście z programu przez exit().
Przykłąd:
EXEC SQL WHENEVER SQLWARNING DO drukuj_ostrzezenie(); EXEC SQL WHENEVER NOT FOUND BREAK; for (;;) { printf("Podaj imie zwierzaka: "); scanf("%s", &id); EXEC SQL SELECT waga INTO :waga FROM Zwierz WHERE imie = :id; printf("Zwierzak %s waży %d.\n", id, waga); }