Poradnik PostgreSQL
1.
Wprowadzenie
Wprowadzenie do PostgreSQL
Większość deweloperów zapytana o różne rozwiązania bazodanowe
najprawdopodobniej wymieni dwa najpopularniejsze i najbardziej efektywne. Będą
to MySQL oraz PostgreSQL, dokładniej opisany w tym dokumencie.
Dyskusja o różnicach i zaletach obu systemów bazodanowych może zamienić się w
długą debatę, jednak należy zwrócić uwagę, że PostgreSQL jest bardziej związany
z relacyjnym modelem przechowywania i dostępu do danych. Wydawało by się że
standardowa funkcjonalność jak FOREIGN KEY powinna być zapewniona w
każdym systemie SQL i tak jest, ale MySQL doczekał się jej dopiero w wersji
5.0. Niezależnie od zapewnianej funkcjonalności i zalet obu rozwiązań, w tym
poradniku znajdują się jedynie porady dotyczące PostgreSQL, będą one z
pewnością cennym źródłem informacji dla każdego posługującego się tym systemem
bazodanowym. Pierwszym krokiem jest oczywiście instalacja, w Gentoo sprowadza
się ona do wykonania polecenia emerge. W kolejnych rozdziałach zostanie
opisany proces instalacji oraz podstawowej konfiguracji.
Instalacja PostgreSQL
Pierwszym krokiem jaki należy wykonać jest instalacja pakietu PostgreSQL.
Wpierw najlepiej jednak wykonać następujące polecenie, by przekonać się że
wszystkie opcje są właściwie ustawione:
Listing 1.1: Sprawdzenie dostępnych flag USE w pakiecie PostgreSQL |
# emerge -pv postgresql
These are the packages that I would merge, in order:
Calculating dependencies ...done!
[ebuild N ] dev-db/postgresql-8.0.4 -doc -kerberos +nls +pam +perl -pg-intdatetime +python
readline (-selinux) +ssl -tcl +xml +zlib 0 kB
|
Poniżej znajduje się lista opcji wraz z krótkimi opisami:
| flaga USE |
Znaczenie |
| doc |
Włącza lub wyłącza instalację dokumentacji z wyłączenie stron podręcznika
man. Jedynym powodem, aby nie używać tej flagi USE jest tylko mała ilość
wolnej przestrzeni dyskowej lub alternatywna metoda dostępu do dokumentacji
PostgreSQL (np. strona www).
|
| kerberos |
W czasie nawiązywania połączenia z bazą danych, w przypadku gdy ta opcji
jest używana, administrator może skorzystać z metody autoryzacji
kerberos by zweryfikować użytkowników lub usługi, które chcą uzyskać
dostęp.
|
| nls |
W przypadku używania tej opcji, PostgreSQL będzie używał tłumaczeń
komunikatów, co może być pomocne dla użytkowników nie posługujących się
językiem angielskim.
|
| pam |
Odpowiednia konfiguracja serwera PostgreSQL oraz aktywacja tej flagi USE
umożliwia administratorowi użycie PAM (Pluggable Authentication
Module) jako sposobu autoryzacji w czasie nawiązywania połączeń przez
użytkowników z bazą danych.
|
| perl |
Włączenie tej opcji spowoduje zbudowanie wiązań PostgreSQL dla języka
perl.
|
| pg-intdatetime |
Wsparcie w PostgreSQL dla 64-bitowych typów reprezentujących datę.
|
| python |
Włączenie tej opcji spowoduje zbudowanie wiązań PostgreSQL dla języka
python.
|
| readline |
Opcja odpowiedzialna za wsparcie w PostgreSQL dla biblioteki
readline. Ułatwia posługiwanie się terminalem, dodaje historię oraz
opcję wyszukiwania.
|
| selinux |
Włączenie tej opcji spowoduje instalację polis selinux dla
PostgreSQL.
|
| ssl |
Załączenie tej flagi USE umożliwi wykorzystanie przez PostgreSQL biblioteki
OpenSSL służącej do szyfrowania połączeń pomiędzy serwerami
PostgreSQL i ich klientami.
|
| tcl |
Włączenie tej opcji spowoduje zbudowanie wiązań PostgreSQL dla języka
tcl.
|
| xml |
Włączenie tej opcji zapewni wsparcie dla XPATH w xml. Więcej
informacji na temat języka xml w PostgreSQL można znaleźć w dokumencie PostgreSQL and
XML (język angielski).
|
| zlib |
Opcja ta nie jest używana przez sam PostgreSQL, ale przez pg_dump.
Umożliwia kompresję tworzonych obrazów danych.
|
Po włączeniu odpowiednich flag, tak by odpowiednio dostosować konfigurację
kompilacji PostgreSQL, czas na kolejny krok - instalację:
Listing 1.2: Instalacja PostgreSQL |
# emerge postgresql
>>> /usr/lib/libecpg.so.5 -> libecpg.so.5.0
>>> /usr/bin/postmaster -> postgres
* Make sure the postgres user in /etc/passwd has an account setup with /bin/bash as the shell
*
* Execute the following command
* emerge --config =postgresql-8.0.4
* to setup the initial database environment.
*
>>> Regenerating /etc/ld.so.cache...
>>> dev-db/postgresql-8.0.4 merged.
|
Jak pokazano na wydruku, po poprawnej kompilacji i instalacji PostgreSQL należy
jeszcze wykonać dodatkowe czynności przygotowujące bazę danych do pracy. W
kolejnym rozdziale zostanie opisany proces konfiguracji.
2.
Konfiguracja PostgreSQL
Stworzenie bazowego środowiska dla bazy danych
Po zakończonej sukcesem instalacji ebuild informuje nas o potrzebie stworzenia
środowiska bazowego, tak by móc uruchomić usługę. Jednak przed wykonaniem
podanego polecenia należy jeszcze zająć się jedną sprawą. W przeciwieństwie do
MySQL, w przypadku PostgreSQL hasło administratora jest takie samo jak
użytkownika w systemie. Jest on tworzony przez ebuild, ale jego hasło
nie zostaje ustawione. Tak więc przed by móc rozpocząć właściwą
konfigurację musimy ustawić hasło dla użytkownika postgres:
Listing 2.1: Ustawienie hasła |
# passwd postgres
New UNIX password:
Retype new UNIX password:
passwd: password updated successfully
|
Po ustawieniu odpowiedniego hasła można przystąpić do stworzenia podstawowego
środowiska PostgreSQL:
Listing 2.2: Konfiguracja środowiska bazodanowego poleceniem emerge --config |
# emerge --config =postgresql-8.0.4
Configuring pkg...
* Creating the data directory ...
* Initializing the database ...
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locale C.
fixing permissions on existing directory /var/lib/postgresql/data ... ok
creating directory /var/lib/postgresql/data/global ... ok
creating directory /var/lib/postgresql/data/pg_xlog ... ok
creating directory /var/lib/postgresql/data/pg_xlog/archive_status ... ok
creating directory /var/lib/postgresql/data/pg_clog ... ok
creating directory /var/lib/postgresql/data/pg_subtrans ... ok
creating directory /var/lib/postgresql/data/base ... ok
creating directory /var/lib/postgresql/data/base/1 ... ok
creating directory /var/lib/postgresql/data/pg_tblspc ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 1000
creating configuration files ... ok
creating template1 database in /var/lib/postgresql/data/base/1 ... ok
initializing pg_shadow ... ok
enabling unlimited row size for system tables ... ok
initializing pg_depend ... ok
creating system views ... ok
loading pg_description ... ok
creating conversions ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the -A option the
next time you run initdb.
Success. You can now start the database server using:
/usr/bin/postmaster -D /var/lib/postgresql/data
or
/usr/bin/pg_ctl -D /var/lib/postgresql/data -l logfile start
*
* You can use /etc/init.d/postgresql script to run PostgreSQL instead of pg_ctl.
*
|
Po wykonaniu polecenia, podstawowe środowisko, tabele i pliki konfiguracyjne
zostały utworzone. W kolejnej sekcji dowiemy się w jak sprawdzić poprawność
instalacji oraz poznamy sposoby kontroli dostępu do bazy danych przez
użytkowników.
Konfiguracja PostgreSQL
Po stworzeniu bazowego środowiska najlepiej sprawdzić czy wszystkie czynności
zostały prawidłowo wykonanie. Sprawdźmy więc czy start usługi zakończy się
sukcesem:
Listing 2.3: Uruchomienie usługi PostgreSQL |
# /etc/init.d/postgresql start
* Starting PostgreSQL ... [ ok ]
|
Po poprawnym starcie usługi dobrym pomysłem jest jej dodanie do domyślnego
poziomu startowego, co spowoduje uruchomienie systemu bazodanowego w czasie
startu systemu:
Listing 2.4: Dodanie demona PostgreSQL do domyślnego poziomu startowego |
# rc-update add postgresql default
* postgresql added to runlevel default
|
Kolejnym krokiem po uruchomieniu demona PostgreSQL jest przetestowanie samego
systemu bazodanowego. Na początek skorzystamy z polecenia createdb, by
utworzyć testową bazę danych. Użyjemy opcji -U tak by ustawić
użytkownika (domyślnie jest to ten aktualnie zalogowany) oraz -W by
wymusić interaktywne podanie stworzonego wcześniej hasła. Ostatnim parametrem
jest nazwa bazy danych, którą chcemy utworzyć:
Listing 2.5: Stworzenie bazy danych poleceniem createdb |
$ createdb -U postgres -W test
Password:
CREATE DATABASE
|
Testowa baza danych została utworzona, można teraz zadać kilka podstawowych
zapytań. Kolejnym krokiem weryfikującym poprawność instalacji, będzie jej
usunięcie, używamy polecenia dropdb. Nową, właściwą bazę danych
utworzymy później:
Listing 2.6: Usunięcie bazy danych poleceniem dropdb |
$ dropdb -U postgres -W test
Password:
DROP DATABASE
|
Domyślna, po wykonaniu wszystkich powyższych czynności, konfiguracja zezwala
jedynie użytkownikowi postgres na wydawanie poleceń. Nie jest to jednak zbyt
wygodne, szczególnie w środowisku wieloużytkowym. W kolejnej sekcji zajmiemy się
stworzeniem oraz ustawieniami kont użytkowników.
Tworzenie kont użytkowników
Jak wcześniej wspomniano, konieczność używania użytkownika postgres by połączyć
się z bazą danych jest trochę uciążliwa, szczególnie w środowisku
wieloużytkowym. W większości przypadków skorzystać z serwera będzie chciało co
najmniej kilku użytkowników, dodatkowo posiadających różne uprawnienia. By
zaradzić temu problemowi można skorzystać z polecenia createuser lub
wydać serię zapytań SQL. Warto jednak skorzystać z tej pierwszej możliwości jako
że jest o wiele bardziej elastyczna z punktu widzenia administratora. Zaczniemy
od stworzenia dwóch użytkowników, administratora mogącego dodawać kolejnych
użytkowników i zarządzać bazą danych oraz zwykłego użytkownika:
Listing 2.7: Tworzenie konta administratora |
$ createuser -a -d -P -E -U postgres -W chris
Enter password for new user:
Enter it again:
Password:
CREATE USER
|
Po wykonaniu powyższych poleceń stworzony został użytkownik posiadający
uprawnienia administratora. Flaga -a oznacza, że może on dodawać
kolejnych użytkowników, -d nadaje mu natomiast uprawnienia do tworzenia
nowych baz danych. Korzystając z opcji -P można interaktywnie wprowadzić
hasło dla tworzonego konta, natomiast dzięki fladze -E hasło zostanie
zaszyfrowane, aby zwiększyć bezpieczeństwo. Po utworzeniu konta administratora
nadszedł czas by je przetestować i stworzyć nowe konto dla zwykłego
użytkownika:
Listing 2.8: Tworzenie zwykłego konta o nazwie testowy |
$ createuser -A -D -P -E -U chris -W testowy
Enter password for new user:
Enter it again:
Password:
CREATE USER
|
Sukces! Po wykonaniu powyższego polecenia został stworzony zwykły użytkownik.
Przetestowaliśmy także uprawnienia superużytkownika. Flagi -A i
-D mają odwrotne znaczenie niż odpowiednio -a oraz -d, w
zamian uniemożliwiając tworzenie kolejnych użytkowników i baz danych. Skoro
dostęp od serwera bazodanowego ma już większe grono użytkowników i stworzyliśmy
mu bazę danych do pracy, pora nauczyć się czegoś o tej pracy.
3.
Używanie PostgreSQL
Konfiguracja praw dostępu
Po wykonaniu wszystkich czynności opisanych do tej pory, system zawiera
użytkownika z uprawnieniami do tworzenia nowych użytkowników i baz danych,
domyślnego superużytkownika, który nie posiada żadnych ograniczeń oraz jednego
zwykłego użytkownika mogącego tylko połączyć się z serwerem bazodanowym.
Normalnie użytkownicy powinni mieć nie tylko dostęp do samego serwera, ale
także do poszczególnych baz danych, gdzie mogę modyfikować ich zawartość
dodając i usuwając dane. By użytkownik testowy miał takie możliwości, należy
wpierw odpowiednio skonfigurować odpowiednie prawa. Jest to bardzo proste
dzięki poleceniu createdb oraz fladze -O. Rozpoczniemy od
stworzenia nowej bazy danych MojaBD, której właścicielem będzie
użytkownik testowy:
Listing 3.1: Tworzenie bazy danych MojaBD |
$ createdb -O testowy -U chris -W MojaBD
Password:
CREATE DATABASE
|
Skoro stworzyliśmy już nową bazę danych o nazwie MojaBD, umożliwiając
użytkownikowi testowy swobodny dostęp do niej to nadeszła najwyższa pora, aby
sprawdzić czy wszystko zostało poprawnie wykonanie. By zalogować się do bazy
danych wystarczy użyć programu psql, który umożliwia dostęp z poziomu
linii poleceń. Można to zrobić postępując zgodnie z instrukcjami znajdującymi
się na poniższym listingu:
Listing 3.2: Logowanie do bazy danych MojaBD jako użytkownik testowy |
$ psql -U testowy -W MojaBD
Password:
Welcome to psql 8.0.4, the PostgreSQL interactive terminal.
Type: \copyright for distribution terms
\h for help with SQL commands
\? for help with psql commands
\g or terminate with semicolon to execute query
\q to quit
MojaBD=>
|
Teraz testowy jest zalogowany do bazy danych i może rozpocząć wydawanie
kolejnych poleceń. By zapoznać się z podstawowymi funkcjami udostępnianymi
przez PostgreSQL w kolejnym rozdziale przyjrzymy się kilku podstawowym
poleceniom klienta psql oraz SQL.
Podstawowe polecenia PostgreSQL i zapytania SQL
Dla tych wszystkich, którzy poznali już MySQL jest to z pewnością bardzo ważna
część poradnika. Znajdują się tutaj nie tylko wyjaśnienia poszczególnych
poleceń, ale także i ich odpowiedniki dla MySQL oraz różnice i unikalne
właściwości charakterystyczne tylko dla PostgreSQL. Zacznijmy od podstawowych
poleceń oferowanych przez klienta psql:
| Polecenie |
Zastosowanie |
Odpowiednik MySQL |
| \c[onnect] [DBNAME|- [USER]] |
Połącz się z inną bazą danych |
USE DATABASE |
| \q |
Zamknij klienta psql
|
quit |
| \i FILE |
Uruchom polecenia znajdujące sie w pliku FILE
|
source FILE |
| \o [FILE] |
Zapisz wynik zapytania w pliku FILE
|
INTO OUTFILE, (zapisuje wszystko, nie tylko wynik zapytania SELECT) |
| \d [NAME] |
Tworzy opis danej bezy danych, tabeli (także innych elementów) |
DESC(RIBE) |
| \db [PATTERN] |
Lista dostępnych baz danych pasujących do wzorca PATTERN (lub
wszystkie gdy go nie podano) |
SHOW TABLES |
Z wyjątkiem polecenia \c[onnect], pozostałe zostaną lepiej opisane w
kolejnych sekcjach tego dokumentu. Jak na razie stworzona baza danych MojaBD
jest pusta, jednak by móc zacząć dodawać do niej dane należy wpierw stworzyć
tabelę. W tym celu można użyć jednego z zapytań języka SQL - CREATE
TABLE. Tabela będzie zawierać informacje na temat poszczególnych produktów i
zawierać pola produkt_id, opis oraz cena:
Listing 3.3: Tworzenie tabeli produktów |
MojaBD=> CREATE TABLE produkty (
MojaBD(> produkt_id SERIAL,
MojaBD(> opis TEXT,
MojaBD(> cena DECIMAL
MojaBD(> );
NOTICE: CREATE TABLE will create implicit sequence "produkty_produkt_id_seq"
for serial column "produkty.produkt_id"
CREATE TABLE
|
Uwaga NOTICE jest nieszkodliwa, więc nie trzeba się szczególnie nią przejmować.
Patrząc na wynik polecenia, szczególnie wpis CREATE TABLE sugeruje, że
operacja zakończyła się sukcesem. Dla pewności jednak najlepiej sprawdzić, czy
tabela została stworzona poprawnie używając polecenia \d:
Listing 3.4: Rzut okiem na nową tabele, aby upewnić się, że została ona stworzona poprawnie |
MojaBD=> \d produkty
Table "public.produkty"
Column | Type | Modifiers
-------------+---------+------------------------------------------------------------------
produkt_id | integer | not null default nextval('public.produkty_produkt_id_seq'::text)
opis | text |
cena | numeric |
|
Po oglądnięciu powyższego listingu można łatwo stwierdzić czy wszystko zostało
poprawnie wykonanie. Teraz skoro baza danych zawiera tabelę, należy zapełnić ją
danymi. Kolejnej sekcja poradnika będzie właśnie tego dotyczyła.
Wprowadzanie danych do bazy danych
W tej części poradnika przyjrzymy się dwóm sposobom wprowadzania danych do nowo
stworzonej tabeli. Najbardziej powszechną metodą jest użycie jednego z
podstawowych poleceń SQL, INSERT:
Listing 3.5: Składnia INSERT |
INSERT INTO [nazwatabeli] (kolumna1,kolumna2,kolumna3) VALUES(wartość1,wartość2,wartość3)
|
nazwatabeli jest nazwą tabeli do której dane mają zostać dopisane,
(kolumna1,kolumna2,kolumna3) pozwala na określenie poszczególnych kolumn, które
mają zawierać wartości określone przez VALUES(wartość1,wartość2,wartość3).
Kolejność jest tu istotna i tak wartość1 zostanie przypisana kolumnie kolumna1,
wartość2 kolumnie kolumna2 itd. Ilość kolumn i wartości musi się
zgadzać, w przeciwnym przypadku zostanie zgłoszony błąd. Przykładowe wywołanie
może wyglądać w następujący sposób.
Ważne:
Z długiego doświadczenia jako użytkownika różnych systemów bazodanowych,
polecam używanie tylko wyrażeń INSERT zgodnie ze składnią podaną
powyżej. Deweloperzy bardzo często korzystają z INSERT INTO nie podając
identyfikatorów kolumn. Może to prowadzić do problemów i błędów w przypadku,
gdy dana tabela zostanie zmodyfikowana i zostaną dodane do niej nowe kolumny.
Skutkiem będzie błąd spowodowany różną ilością kolumn w tabeli i określonych
wartości w zapytaniu SQL. Należy zawsze podać nazwy kolumn, chyba że
mamy 300% pewność, że struktura tabeli nigdy nie zostanie zmieniona.
|
Listing 3.6: Dodawanie danych do tabeli |
MojaBD=> INSERT INTO produkty (opis,cena) VALUES('produkt testowy', 12.00);
INSERT 17273 1
|
Ostatnia linia wymaga odrobiny wyjaśnień. W rezultacie wykonania zapytania
insert zwrócony zostanie numer OID (Object Identifier) oraz numer wiersza do,
którego dane zostały dopisane. OID nie będzie omówiony w ramach tego poradnika,
szczegółowe informacje na ten temat zawiera podręcznik
PostgreSQL (język angielski). Teraz postawmy się w sytuacji gdy musimy
wprowadzić do tabeli dużą ilość produktów, wprowadzanie poszczególnych wierszy
ręcznie może być bardzo czasochłonne. Na szczęście w takich przypadkach można
posłużyć się poleceniem COPY, które umożliwia wprowadzenia danych z
pliku lub standardowego wejścia. Załóżmy, że dane są zawarte w pliku tekstowym
csv (comma separated values), który zawiera wszystkie potrzebne informacje:
produkt_id, opis, cena. Przykładowy plik może wyglądać następująco:
Listing 3.7: produkty.csv |
2,ser,6.79
3,zupa,0.69
4,woda,1.79
|
Skorzystamy w tym przypadku z polecenia COPY, by przepisać dane z pliku
do tabeli w bazie danych:
Ważne:
Polecenie COPY FROM STDIN zostało użyte tylko z tego względu, że tylko
użytkownik postgres może wczytać dane z pliku (z przyczyn bezpieczeństwa).
|
Listing 3.8: Użycie COPY by zapełnić tabele produktów danymi |
MojaBD=> COPY produkty FROM STDIN WITH DELIMITER AS ',';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>> 2,ser,6.79
>> 3,zupa,0.69
>> 4,woda,1.79
>> \.
|
Niestety w tym przypadku po wykonaniu powyższego polecenia na wydruku nie
zobaczymy podobnych informacji jak to miało miejsce w przypadku INSERT
INTO. Więc w jaki sposób można się przekonać, że dane zostały rzeczywiście
wprowadzone do tabeli? W kolejnej części poradnika można znaleźć odpowiedź na
to pytanie.
Zapytania SQL w PostgreSQL
W tej części poradnika, będzie można znaleźć opis oraz przykłady użycia
zapytania SQL SELECT. Podstawowa składnia wygląda następująco:
Listing 3.9: Składnia SELECT |
SELECT (kolumna1,kolumna2|*) FROM (tabela) [WHERE (warunki)]
|
Istnieją dwa sposoby na wybranie interesujących nas kolumn. Można albo użyć
znaku * by wybrać wszystkie kolumny jakie zawiera dana tabela lub podać
listę nazw poszczególnych kolumn. Szczególnie ta druga metoda jest bardzo
interesująca, gdyż pozwala zawęzić ilość zwracanych danych, co jest szczególnie
ważne w przypadku gdy mamy do czynienia z rozbudowaną strukturą tabeli.
Pierwszym krokiem będzie wykorzystanie polecenia SELECT razem z
*:
Listing 3.10: Odczyt danych z tabeli produkty |
MojaBD=> SELECT * FROM produkty;
produkt_id | opis | cena
------------+---------------- +-------
1 | produkt testowy | 12.00
2 | ser | 6.79
3 | zupa | 0.69
4 | woda | 1.79
(4 rows)
|
Listing zawiera wszystkie wcześniej wprowadzone do tabeli dane. By zwiększyć
czytelność najlepiej określić interesujące nas kolumny. Powiedzmy, że
interesują nas jedynie cena i opis danego produktu, jego identyfikator w tym
przypadku może zostać pominięty:
Listing 3.11: Odczyt wybranych wierszy z tabeli produkty |
MojaBD=> SELECT opis,cena FROM produkty;
opis | cena
-----------------+-------
produkt testowy | 12.00
ser | 6.79
zupa | 0.69
woda | 1.79
(4 rows)
|
Po zmodyfikowaniu zapytania na wydruku widzimy jedynie cenę i nazwę produktów,
co pozwala skupić się jedynie na ważnych informacjach. Kolejnym krokiem będzie
zawężenie informacji tylko do tych produktów, których cena jest wyższa niż 2
złote. By tego dokonać bardzo pomocne jest słowo kluczowe WHERE.
Listing 3.12: Odczyt wybranych wierszy z tabeli produkty |
MojaBD=> SELECT opis,cena FROM produkty WHERE cena > 2.00;
opis | cena
-----------------+-------
produkt testowy | 12.00
ser | 6.79
(2 rows)
|
Wydruk zawiera teraz tylko listę produktów o cenie większej 2 złotych,
zmniejszając jeszcze bardziej ilość wyświetlonych informacji. Takie metody
odpytywania stanowią potężne narzędzie i mogą pomóc w stworzeniu przydatnych
raportów.
Podsumowanie
Wielkie podziękowania należą sie Masatomo Nakano, opiekunowi pakietu postgresql
w Gentoo, który służył poradą i odpowiedziami na wiele pytań. Wszelkie sugestie
na temat zawartych informacji merytorycznych prosimy kierować na adres
Chris White. Bardziej szczegółową dokumentację można
znaleźć się na oficjalnej stronie
PostgreSQL.
Materiał udostępniany na podstawie licencji Creative Commons -
Attribution / Share Alike.
|