R-04-t.doc

(250 KB) Pobierz
PLP - Rozdział 4: Interfejsy PostgreSQL

 

4. Interfejsy PostgreSQL

Po zapoznaniu się z podstawowym sposobem interaktywnego dostępu do bazy PostgreSQL z interpretera psql wykorzystującego język SQL, możemy zająć się sprawą dostępu do bazy danych z kodu programu. Odbywa się to bardzo podobnie i wszystkie znane polecenia używane dotychczas w wierszu poleceń działają w kodzie prawie natychmiast.

Dostęp do PostgreSQL z kodu programu

Do baz PostgreSQL można uzyskać dostęp z wielu języków programowania. Autorzy znają przynajmniej takie:

q       C

q       C++

q       Java

q       Perl

q       Python

q       PHP

q       Tcl

Prawdopodobnie istnieją także inne języki obsługujące PostgreSQL, o których nic nawet nie wiemy.

Istnieje także sterownik ODBC, który umożliwia dostęp z wielu innych systemów, włącznie z klientami MS Windows, które mogą korzystać ze źródeł danych ODBC, takimi jak na przykład Access.

Głównym językiem używanym w przykładach w naszej książce jest język C i z tego właśnie języka chcemy mieć dostęp do naszej bazy danych PostgreSQL. Istnieją jednak dwie metody uzyskiwania dostępu do PostgreSQL z kodu programu.

q       Pierwszą jest utrzymana w tradycyjnym stylu biblioteka o nazwie libpq. W celu uzyskania dostępu do bazy trzeba wywoływać funkcje biblioteczne z kodu programu.

q       Druga metoda polega na osadzaniu poleceń SQL w kodzie języka C i ich przetwarzaniu przez preprocesor przed ostateczną kompilacją. Według terminologii PostgreSQL nazywa się to ecpg. Jest to podejście bardzo podobne do użycia preprocesora języka C przetwarzającego polecenia #include i #define przed przekazaniem kodu do głównego kompilatora. Zagadnienie to jest z pewnością znane użytkownikom niektórych komercyjnych produktów, takich jak PRO*C firmy Oracle i ESQLC firmy Informix ponieważ wszystkie z nich spełniają w większym lub mniejszym stopniu standard ANSI opisujący osadzanie poleceń SQL.

W tym rozdziale pokażemy praktyczne zastosowanie obydwu metod, co ułatwi dokonanie wyboru metody najwłaściwszej dla potrzeb użytkownika lub najwygodniejszej do pracy.

Biblioteka libpq

Ogólnie mówiąc, funkcje występujące w bibliotece libpq można pogrupować na trzy następujące kategorie:

q       Funkcje do zarządzanie połączeniami.

q       Funkcje służące do uruchamiania poleceń SQL.

q       Funkcje zwracające wyniki zapytań.

Omówimy te grupy po kolei. W bibliotece libpq przez lata zgromadziły się także niektóre przestarzałe funkcje, które są utrzymywane tylko dla zachowania zgodności ze starymi wersjami. Nie będziemy się tu nimi zajmować i pokażemy tylko te, które powinny być używane w nowszych programach. Jeśli przy przeglądaniu jakiegoś starszego kodu libpq spostrzeżemy nieznane wywołania funkcji bibliotecznych, to zawsze możemy zapoznać się z nimi w dokumentacji dostępnej na stronie internetowej PostgreSQL pod adresem: http://www.postgresql.org.

Aby skorzystać z którejś funkcji libpq należy:

q       Dołączyć plik nagłówkowy libpq-fe.h.

q       Dopisać katalog pgsql do ścieżki zawierającej biblioteki dołączane podczas kompilacji.

q       Skonsolidować program z biblioteką pq.

Końcówka „fe” w nazwie libpq-fe oznacza interfejs (od słów front-end). Aby skompilować plik używający libpq, należy więc w ogólnym przypadku użyć polecenia:

 

$ gcc -o program -I/usr/include/pgsql program.c -lpg

Oczywiście, szczegółowe parametry zależą od położenia katalogów instalacyjnych w danym systemie. Jeśli znajdują się one w innym miejscu, to trzeba będzie inaczej określić położenie katalogu zawierającego pliki dołączane i wskazać alternatywny katalog biblioteki za pomocą opcji -L/usr/local/pgsql/lib.

Funkcje do obsługi połączeń z bazą danych

Zaleca się połączenia z bazą danych PostgreSQL za pomocą polecenia PQconnectdb. Czasami należy także użyć opcji -i gdy ma być uruchomiony demon postmaster nasłuchujący na gniazdach TCP/IP i domeny UNIX.

 

PGconn *PQconnectdb(const char *conninfo);

Napis conninfo definiujący połączenie może zawierać sekwencję parametrów i wartości oddzielonych spacjami. Jeżeli w samej wartości ma się znaleźć spacja, to należy ją ująć w apostrofy. Parametry, którym nie nadaje się wartości w sposób jawny uzyskują domyślnie wartości NULL. Funkcje biblioteczne uzyskują także wartości domyślne lub wartości zdefiniowane przez zmienne środowiskowe. Można definiować następujące parametry połączenia:

 

host

Nazwa komputera, z którym będzie zestawiane połączenie. Domyślnie jest to komputer lokalny.

port

Numer portu, na którym będzie utrzymywane połączenie. Domyślnie jest to standardowy port używany przez PostgreSQL o numerze 5432.

dbname

Nazwa bazy danych, z którą chcemy się połączyć. Domyślnie jest to taka sama nazwa jak nazwa zalogowanego użytkownika systemu Linux.

user

Nazwa użytkownika bazy danych. Domyślnie jest to nazwa używana przy logowaniu.

password

Hasło dostępu do bazy.

options

Wymagane opcje śledzenia.

tty

Plik lub terminal używany jako wyjście komunikatów o błędach wytwarzanych przez program.

Za każdym parametrem następuje znak równości i następnie wartość tego parametru, czyli np. do połączenia się z bazą template1 na komputerze gw1 należy użyć następującego polecenia:

 

conn = PQconnectdb("host=gw1 dbname=template1");

Wskaźnik o wartości NULL jest zwracany tylko wtedy, gdy biblioteka nie zdoła przydzielić obiektu, z którym ma nastąpić połączenie. Nawet jeżeli otrzymamy wskaźnik nie mający wartości NULL, to nadal trzeba sprawdzić czy połączenie nastąpiło wywołując funkcję PQstatus.

 

ConnStatusType PQstatus(PGconn *conn);

Funkcja ta zwraca jedną z dwóch wartości, albo CONNECTION_OK, albo CONNECTION_BAD, których znaczenie jest ukryte w ich nazwach. Po udanym nawiązaniu połączenia z bazą pozostaje ono w takim stanie, chyba że występują problemy z siecią lub zostanie zamknięta oddalona baza danych.

Jeśli występują problemy z połączeniem, to tekst komunikatu o błędzie można uzyskać wywołując funkcję:

 

char *PQerrorMessage(PGconn *conn);

Funkcja ta zwraca wskaźnik do ustalonego obszaru pamięci, a więc dany tekst komunikatu może być tekstem generowanym przy późniejszych wywołaniach funkcji z biblioteki libpq. Jeżeli trzeba zamknąć połączenie z powodu zakończenia pracy programu lub awarii, to należy czynić to za pomocą funkcji:

 

void PQfinish(PGconn *conn);

Funkcja ta musi być zawsze wywoływana, nawet gdy połączenie się nie udało. Jest to potrzebne dlatego, że nie tylko chodzi w niej o zamkniecie połączenia z bazą, ale także zwolnienie obszaru pamięci i innych zasobów związanych z połączeniem. Niewłaściwie zamknięte połączenie może spowodować, że program będzie wymagał nadmiernego zwiększenia zasobów systemowych.

Po zamknięciu połączenia wskaźnik obiektu połączeniowego nie oznacza niczego sensownego i nie może być przekazywany jako parametr do jakichkolwiek funkcji. Dobrym zwyczajem programisty powinno więc być nadanie mu wartości NULL tuż po wywołaniu funkcji PQfinish.

Znamy już kilka funkcji i możemy napisać pierwszy program służący do połączenia się z serwerem PostgreSQL. Nie będzie on wprawdzie zbyt użyteczny, ponieważ służy tylko do testowania połączenia, ale dzięki niemu uczynimy pierwszy krok. Musimy pamiętać o zmianie nazwy serwera i użytkownika na nazwy używane lokalnie, oraz o utworzeniu bazy danych o nazwie takiej jak nazwa użytkownika (tworzenie bazy opisaliśmy w poprzednim rozdziale).

 

#include <stdlib.h>

#include <stdio.h>

#include <libpq-fe.h>

 

int main()

{

 

   PGconn *conn;

   const char *connection_str = "host=localhost dbname=template1";

 

   conn = PQconnectedb(connection_str);

   if (PQstatus(conn) == CONNECTION_BAD) {

      fprintf(stderr, "Connection to %s failed, %s, connection_str,

PQerrorMessage(conn));

   } else {

      printf("Connected OK\n");

   }

   PQfinish(conn);

   return EXIT_SUCCESS;

}

Program ten bardzo łatwo można przeanalizować: najpierw jest tworzony napis używany jako parametr połączenia z bazą template1 na serwerze localhost, potem następuje próba połączenia, wyświetlenie komunikatu jeżeli wystąpi błąd i zamknięcie połączenia przed zakończeniem działania programu.

Uruchamianie poleceń SQL

Okazuje się niespodziewanie, że uruchamianie zapytań na serwerze jest bardzo proste. Istnieje tylko jedna funkcja służąca do wywołania polecenia i trzy funkcje wykorzystywane do sprawdzenia wyniku i odczytu informacji o błędach. Uruchomienie polecenia SQL odbywa się następująco:

 

PGresult *PQexec(PGconn *conn, const char *sql_string);

Funkcja ta zwraca wskaźnik o wartości NULL tylko w nadzwyczajnych okolicznościach, a więc musi on być przechwytywany, ponieważ w przeciwnym wypadku można otrzymać wyniki na podstawie wskaźnika innej funkcji:

 

ExecStatusType *PQresultStatus(PGresult *result);

Wynik zawiera wyliczenie typu ExecStatusType, które ma jedną z podanych niżej wartości:

 

PGRES_EMPTY_QUERY

Nic nie zostało zrobione.

PGRES_COMMAND_OK

Polecenie zostało wykonane poprawnie, lecz żadne dane nie zostały zwrócone ponieważ nie było to polecenie SELECT.

PGRES_TUPLES_OK

Polecenie zostało wykonane poprawnie i jakieś dane mogły być zwrócone.

PGRES_COPY_OUT

Trwa operacja kopiowania do pliku zewnętrznego.

PGRES_COPY_IN

Trwa operacja kopiowania z pliku zewnętrznego.

PGRES_BAD_RESPONSE

Zdarzyło się coś nieoczekiwanego.

PGRES_NONFATAL_ERROR

Wystąpił błąd niekrytyczny.

PGRES_FATAL_ERROR

Wystąpił błąd krytyczny.

Zwróćmy szczególną uwagę na definicję PGRES_TUPLES_OK. Otrzymanie takiej odpowiedzi oznacza, że polecenie SELECT zostało wykonane poprawnie, ale jednocześnie nie znaczy to, że jakieś dane zostały zwrócone. W następnym podrozdziale zobaczymy, jak sprawdzić czy rzeczywiście dane zostały zwrócone. Błędy typu COPY odnoszą się od ładowania bazy danych lub tworzenia kopii zapasowej tej bazy.

Jeśli chcemy otrzymać tekst komunikatu o błędzie, to potrzebujemy:

 

const char *PQresultErrorMessage(PGresult *result);

Zauważmy, że tekstowy komunikat o błędzie otrzymujemy tu w inny sposób niż przy funkcjach obsługujących połączenia, gdzie stosuje się PQerrorMessage.

Często warto jest znać liczbę wierszy, na które zdziałało polecenie SQL. Dotyczy to zwłaszcza polecenia DELETE, ponieważ PostgreSQL traktuje jako udane także wykonanie poprawnego składniowo polecenia, które faktycznie nie usunie żadnych wierszy.

Liczbę wierszy objętych poleceniami INSERT, UPDATE i DELETE można znaleźć za pomocą PQcmdTuples.

 

const char *PQcmdTuples(PGresult *result);

Zwróćmy uwagę na to, że takie wywołanie zwraca char *result, czyli ciąg cyfr kończący się wartością NULL w formacie znakowym, a nie liczbę całkowitą, której można się spodziewać. Wyznaczenie liczby wierszy objętych przez polecenie ...

Zgłoś jeśli naruszono regulamin