r 05 07 CYDPC3TZ5GS5LDLS7BMCZZQJMYG4VPCYDMK4AVA


Rozdział 5. MySQL
Zdecydowaliśmy wcześniej, że MySQL nie będzie idealnym rozwiązaniem dla bazy danych
obsługującej naszą aplikację. Istnieją jednak zastosowania, gdzie ta popularna baza danych spełnia
znakomicie swoją rolę  tam, gdzie ważna jest szybkość działania i nie trzeba używać transakcji
lub innych zaawansowanych właściwości języka SQL. Dlatego właśnie chcemy przedstawić bazę
danych MySQL.
W tym rozdziale omówimy instalację MySQL, a następnie zajmiemy się następującymi
zagadnieniami:
Podstawowe polecenia używane przez administratora podczas instalacji MySQL.
Główne różnice między wspomaganiem języka SQL w PostgreSQL i w MySQL.
Dostęp do bazy danych MySQL z programu w języku C.
Instalacja i zarządzanie
Instalacja MySQL jest bardzo prosta. Jeżeli w danej dystrybucji Linuksa nie ma kopii
dystrybucyjnej MySQL, to można ją pobrać ze strony macierzystej http://www.mysql.com.
Znajdują się tam zarówno pakiety skompilowane (w tym także RPM), jak i pliki zródłowe dla
wielu systemów. W zasadzie wstępnie skompilowane pakiety powinny całkowicie spełniać
oczekiwania, ale jeśli ktoś chce skompilować je we własnym zakresie, może pobrać wersję
zródłową.
Pakiety skompilowane
Pakiety RPM są aktualnie rozpowszechniane w czterech częściach:
Główny pakiet serwera (ang. main server package) o nazwie:
MySQL-..rpm
Zawiera on główne pliki binarne i instrukcje oraz wielojęzyczne pliki pomocnicze. Ten
pakiet trzeba zainstalować.
Pakiet kliencki (ang. client package) o nazwie mającej następującą postać:
MySQL-client-..rpm
Zawiera on podstawowe programy klienta, które mają współpracować z serwerem.
Programy te tworzą oddzielny pakiet, ponieważ w przypadku instalacji na kilku
komputerach pracujących tylko jako klienty bazy MySQL nie trzeba instalować
składników serwera na wszystkich komputerach.
Pakiet zawierający składniki współdzielone (ang. shared component package) o nazwie:
MySQL-shared-..rpm
zawierający współdzielone biblioteki wymagane przez niektóre klienty.
Pakiet rozwojowy (ang. development package) o nazwie:
MySQL-devel-..rpm
zawierający pliki nagłówkowe i dodatkowe biblioteki potrzebne przy budowie aplikacji,
które komunikują się z serwerem MySQL.
Jeżeli ktoś decyduje się na tworzenie programów korzystających z dostępu do bazy MySQL, to
powinien na swoim komputerze zainstalować wszystkie wyżej wymienione pakiety.
Skrypt instalacyjny automatycznie tworzy wstępną bazę danych. Potrzebny będzie także skrypt o
nazwie mysql służący do uruchamiania i zatrzymywania serwera, przechowywany w katalogu
init.d. Najlepszym sposobem na odszukanie plików bazy jest sprawdzenie zawartości tego
skryptu. Są tam podane standardowe ścieżki i definicje, np. po zainstalowaniu binarnego pakietu
RPM w dystrybucji Red Hat pewien fragment skryptu mysql o nazwie shell variables wygląda
następująco:
bindir=/usr/bin
datadir=/var/lib/mysql
pid_file=/var/lib/mysql.pid
mysql_daemon_user=mysql # Run mysql as this user.
Jak widać, jest to łatwe do zrozumienia. Podczas instalacji jest również tworzony użytkownik o
nazwie  mysql , w imieniu którego będzie uruchamiany demon serwera MySQL. W zależności od
wersji skrypt instalacyjny może od razu uruchomić serwer. Sprawdzamy to za pomocą
następującego polecenia:
$ ps -el | grep mysql
Jeżeli będą widoczne jakieś procesy mysqld, oznacza to, że serwer został uruchomiony. Jeżeli tak
nie będzie, to należy uruchomić skrypt mysql (z katalogu init.d) z parametrem start jako
użytkownik mysql. W zależności od dystrybucji polecenie może wyglądać jak niżej:
# su - mysql
$ /etc/rc.d/init.d/mysql start
Budowanie z plików zródłowych
Instalacja z plików zródłowych jest tylko trochę trudniejsza niż instalacja pakietów binarnych. Po
pobraniu i rozpakowaniu plików zródłowych należy użyć polecenia:
$ ./configure --help
W ten sposób sprawdzamy, czy trzeba zmieniać jakieś opcje konfiguracyjne. Przy założeniu, że
ustawienia domyślne są wystarczające, sekwencja poleceń używanych do kompilacji i instalacji
serwera jest następująca:
$ ./configure
$ make
$ su -
# make install
Jeśli wszystko przebiegnie prawidłowo, to należy uruchomić (jako root) skrypt instalacyjny
scripts/mysql_install_db w celu wstępnego zainicjowania serwera:
# scripts/mysql_install_db
W zależności od wersji MySQL skrypt może uruchamiać automatycznie serwer bazy danych. Jeśli
tak nie jest, wówczas należy uruchomić serwer ręcznie za pomocą skryptu mysql.server
umieszczonego w katalogu z plikami pomocniczymi. Skrypt mysql_install_db tworzy niektóre
wymagane tabele bazy danych i inicjuje uprawnienia do plików. Ma on prostą budowę i
zainteresowani mogą przejrzeć jego zawartość, chcąc się dokładniej zorientować, co on robi.
Przed zakończeniem działania skryptu pojawi się komunikat informujący o sposobie
automatycznego uruchomienia MySQL podczas rozruchu systemu. Oprócz tego pojawi się
przypomnienie o konieczności ustawienia hasła przez użytkownika bazy MySQL o nazwie  root .
Jest to dosyć mylące, ponieważ w MySQL użytkownik o nazwie root jest administratorem serwera
bazy danych. Jeżeli te komunikaty nie pojawią się, to nie należy się tym przejmować i w razie
potrzeby odszukać je w skrypcie instalacyjnym.
Ostatnią czynnością podczas instalacji z plików zródłowych jest skonfigurowanie serwera w taki
sposób, aby mógł on być automatycznie uruchamiany i zatrzymywany przez system. W katalogu z
plikami pomocniczymi (support-files) można znalezć pomocny skrypt mysql.server, który
należy skopiować do katalogu init.d, a następnie utworzyć dowiązania do odpowiednich plików
w rc.d. Jako alternatywny sposób uruchamiania można zawsze użyć ręcznego wywołania tego
skryptu z parametrem start lub stop. Za każdym razem należy upewniać się (podobnie jak w
przypadku bazy PostgreSQL), czy przed wyłączeniem systemu został zatrzymany serwer bazy
danych.
Konfiguracja poinstalacyjna
Jeżeli wszystko przebiegło poprawnie, to wówczas mamy utworzoną domyślną konfigurację za
pomocą skryptu mysql_install_db i serwer uruchomiony w trybie demona za pomocą skryptu
z init.d. Sprawdzimy teraz działanie serwera:
$ mysql -u root mysql
Powinien pojawić się komunikat  Welcome to the MySQL monitor i za nim znak zachęty
mysql>. Oznacza to, że serwer działa; jednocześnie widzimy, że każdy może się z nim połączyć i
uzyska uprawnienia administratora. Pracę kończymy wpisując polecenie quit.
Można sprawdzić inaczej, czy serwer działa, używając do tego polecenia mysqladmin:
$ mysqladmin -u root version
Otrzymujemy wówczas informację nie tylko o tym, czy serwer działa, ale także o jego wersji i
czasie pracy.
Jeżeli podczas korzystania z mysql nastąpi przerwa w połączeniu z bazą, to najpierw należy
sprawdzić, szukając procesów mysqld za pomocą polecenia ps, czy serwer faktycznie działa. Jeśli
serwer nie działa, ale jesteśmy pewni, że był uruchamiany, to można próbować użyć programu
safe_mysqld z opcją --log. W katalogu, w którym była zainstalowana baza MySQL
(najczęściej jest to /var/lib/mysql), powstanie wówczas plik logu o nazwie takiej jak nazwa
komputera z końcówką .log.
Jeżeli serwer działa, ale nie można się z nim połączyć, to przeglądając skrypt
mysql_install_db albo init.d/mysql należy sprawdzić, gdzie została zainstalowana baza
danych. Zazwyczaj jest to katalog /var/lib/mysql w podkatalogu mysql. Powinny być tam
pliki o nazwach mających końcówki .frm, .ISD i .ISM.
Jeżeli to się nie uda, to można próbować zatrzymać serwer, ręcznie usunąć pliki bazy danych i
uruchomić ręcznie skrypt mysql_install_db w celu ponownego utworzenia bazy. Potem należy
ponownie uruchomić serwer. Jest nadzieja, że tym razem wszystko się uda, ale jeśli tak nie będzie,
to należy poszukać dodatkowych informacji na temat wyszukiwania błędów w dokumentacji
dostarczanej w pakiecie MySQL. Jeżeli był instalowany pakiet binarny, to dokumentacja znajduje
się zapewne w katalogu /usr/doc/MySQL-. Można także sprawdzić informacje w
Internecie na stronie MySQL.
Bardzo kłopotliwy błąd powstaje wówczas, gdy uprawnienia do plików bazy danych nie są zgodne
z uprawnieniami użytkownika mysql tworzonego automatycznie podczas instalacji albo gdy
demon mysqld został uruchomiony w imieniu użytkownika o niewłaściwym identyfikatorze. W
takim wypadku można połączyć się z bazą, ale nie udaje się uruchomić innych programów, np.
mysqladmin i mysqlshow. Trzeba wówczas dokładnie sprawdzić atrybuty plików bazy danych i
użytkownika, który uruchomił proces mysqld, ponieważ błąd prawdopodobnie jest spowodowany
ich niedopasowaniem. Właścicielem wszystkich plików bazy danych powinien być użytkownik
mysql.
Kolejne zadanie polega na nadaniu hasła administratorowi bazy danych uprawniającego go do
dostępu do serwera. Służy do tego następujące polecenie mysqladmin:
$ mysqladmin -u root password nowe_haslo
Nadaliśmy w ten sposób początkowe hasło dostępu  nowe_haslo i próba połączenia z bazą się nie
uda, chyba że nada się hasło użytkownikowi:
$ mysql -u root -psecretpassword mysql
Zwróćmy tu uwagę na trochę dziwną składnię: między literą p a faktycznym hasłem nie może być
spacji. Ostatni parametr, czyli mysql, jest nazwą bazy danych. Jeżeli hasło nie zostanie wpisane
(czyli wystąpi tylko opcja -p), to mysql zażąda jego podania. Ponieważ podawanie hasła w
wierszu poleceń nie jest bezpieczne (inni użytkownicy mogą je zobaczyć np. za pomocą polecenia
ps), to lepiej jest używać następującej postaci tego polecenia, nie wpisując hasła:
$ mysql -u root -p mysql
Baza zażąda wówczas hasła. Po uruchomieniu mysql można sprawdzić, czy istnieje testowa baza
danych używając polecenia:
mysql> select host, db, user from db;
Powinniśmy otrzymać coś takiego:
+------+----------+------+
| host | db | user |
+------+----------+------+
| % | test | |
| % | test\_% | |
+------+----------+------+
2 rows in set (0.00 sec)
Aby zakończyć pracę z mysql, należy użyć polecenia quit.
Zarządzanie bazą MySQL
W pakiecie MySQL znajduje się kilka programów pomocniczych wspomagających zarządzanie
bazą danych. Najczęściej używany jest program mysqladmin, ale zanim przejdziemy do
omawiania sposobu tworzenia programów-klientów, omówimy skrótowo jeszcze kilka innych
programów administracyjnych.
Polecenia
Wszystkie polecenia oprócz mysqlshow mogą mieć trzy standardowe argumenty:
-u nazwa_użytkownika,
-p [hasło],
-h nazwa_komputera.
Argument -h jest używany przy łączeniu się z serwerem bazy danych działającym na innym
komputerze i na komputerze lokalnym zawsze można go pominąć. Jeżeli argument -p jest podany
bez hasła, to zakłada się, że w danym poleceniu MySQL hasło nie jest potrzebne.
isamchk
Jest to program pomocniczy służący do sprawdzania i naprawy tabel bazy danych MySQL. Należy
go uruchamiać w imieniu pseudoużytkownika (ang. pseudo-user) utworzonego przez MySQL,
przechodząc do odpowiedniego podkatalogu bazy danych, która ma być sprawdzana. Aby np.
sprawdzić bazę fud, należy przejść do katalogu /var/lib/mysql/fud. Program isamchk ma
wiele opcji, które zostaną wypisane po uruchomieniu go bez parametrów.
Ogólnie mówiąc, jeżeli chcemy sprawdzić wszystkie tabele w danej bazie, to program ten należy
uruchomić z jakimiś opcjami, podając dodatkowo parametr *.ISM. Najważniejsze opcje wypisane
są niżej:
-a
Analiza plików.
-e
Rozszerzone sprawdzanie.
-r
Poprawianie znalezionych błędów.
-s
Praca bez komunikatów aż do momentu znalezienia błędu.
Po wywołaniu isamchk bez parametrów można zapoznać się z wyczerpującym opisem tego
programu.
Należy mieć nadzieję, że nie trzeba będzie nigdy używać tego programu, ale gdy zdarzy się
niekontrolowane wyłączenie komputera, może on okazać się pomocny przy odtwarzaniu na dysku
uszkodzonych plików bazy danych. Do takich zadań służy właśnie ten program.
mysql
Jest to standardowe narzędzie wiersza poleceń, które może być używane do wielu zadań
administracyjnych opisanych w następnych częściach rozdziału.
Polecenie mysql ma dodatkowy argument, który musi być podany po wszystkich opcjach. Jest to
nazwa bazy danych, z którą chcemy się połączyć, np. użytkownik rick mający hasło bar
uruchamia polecenie mysql w bazie foo następująco:
$ mysql -u rick -pbar foo
Taki sposób podawania nazwy bazy danych jest dosyć wygodny. Opis wszystkich opcji polecenia
mysql jest wyświetlany po użyciu opcji -h.
Można także zażądać, by program mysql pobierał polecenia z pliku, przekierowując standardowe
wejście na plik, jak w przykładzie poniżej:
$ mysql -u rick -pbar foo < sqlcommands.sql
Po przetworzeniu poleceń pobranych z pliku program mysql kończy działanie.
mysqladmin
Jest to główny program do zarządzania bazą danych. Oprócz normalnych argumentów -u user i
-p obsługuje on cztery podstawowe polecenia:
create databasename
Tworzenie nowej bazy danych.
drop databasename
Usuwanie bazy danych.
password newpassword
Zmiana hasła (tak jak to było opisane wcześniej).
status
Informacja o stanie serwera.
version
Informacja o numerze wersji serwera i czasie jego działania.
Po wywołaniu mysqladmin bez parametrów zostanie wyświetlony opis dostępnych poleceń.
mysqlbug
Mamy nadzieję, że nigdy nie trzeba będzie korzystać z tego programu! Zbiera on informację o
danej instalacji i konfiguracji SQL i tworzy standardowy raport, który może być przesłany do
autorów pakietu łącznie ze szczegółowym opisem problemu.
mysqldump
Jest to bardzo pomocny program, który umożliwia zapis bazy danych do pliku (łącznie ze
wszystkimi lub tylko z wybranymi tabelami). Zapisuje on w pliku standardowe polecenia SQL,
które mogą być uruchomione po przekierowaniu pliku na wejście programu mysql lub
mysqlimport. Jako parametr podaje się nazwę bazy danych i opcjonalnie listę tabel z tej bazy.
Oprócz standardowych opcji -u i -p są jeszcze dwie najczęściej używane:
--add-drop-table
Dołączenie do pliku wyjściowego polecenia SQL usuwającego tabele
przed ich ponownym utworzeniem.
-t
Zapis do pliku tylko danych z tabel.
-d
Zapis do pliku tylko struktury tabel.
Informacja jest przesyłana na standardowe wyjście, a więc prawdopodobnie trzeba będzie
przekierować ją do pliku.
Program mysqldump można wykorzystać jako narzędzie do okresowego tworzenia zapasowych
kopii bazy danych lub przy eksporcie danych do innej bazy. Dane wyjściowe mają postać prostego
pliku ASCII i łatwo je odczytać. Można także dodać do nich własne komentarze. Jeżeli np.
chcemy zachować bazę rick w pliku rick.dump, to powinniśmy użyć następującego polecenia:
$ mysqldump -u rick -p rick > rick.dump
Powstający w wyniku tego plik (przy założeniu, że w bazie istnieje tylko jedna tabela) może
wyglądać następująco:
# MySQL dump 7.1
#
# Host: localhost Database: rick
#--------------------------------------------------------
# Server version 3.22.32-log
#
# Table structure for table 'children'
#
CREATE TABLE children (
childno int(11) DEFAULT '0' NOT NULL auto_increment,
fname varchar(30),
age int(11),
PRIMARY KEY (childno)
);
#
# Dumping data for table 'children'
#
INSERT INTO children VALUES (1,'Jenny',14);
INSERT INTO children VALUES (2,'Andrew',10);
INSERT INTO children VALUES (3,'Gavin',4);
INSERT INTO children VALUES (4,'Duncan',2);
INSERT INTO children VALUES (5,'Emma',0);
INSERT INTO children VALUES (6,'Alex',11);
INSERT INTO children VALUES (7,'Adrian',5);
mysqlimport
Program mysqlimport uzupełnia się z programem mysqldump i służy do tworzenia bazy danych
z pliku tekstowego (oczywiście, plik ten nie musi być koniecznie utworzony za pomocą programu
mysqldump, można utworzyć go także ręcznie). Program mysqlimport wymaga podania jedynie
nazwy pliku tekstowego, z którego mają być odczytywane polecenia.
Polecenia SQL zapisane w pliku może także odczytywać program mysql.
mysqlshow
Program mysqlshow jest wygodnym narzędziem do wyświetlania informacji o serwerze, bazie
danych i o tabeli  zależy to od użytych opcji:
Program wywołany bez argumentów wyświetla listę istniejących baz danych.
Jeżeli jako argument zostanie podana nazwa bazy danych, to program wyświetli listę
tabel w tej bazie.
Jeśli zostanie podana nazwa bazy danych i nazwa tabeli, to program wyświetli listę nazw
kolumn w tej tabeli.
Jeżeli poda się nazwę bazy danych, nazwę tabeli i nazwę kolumny, to zostanie
wyświetlona zawartość podanej kolumny.
Nie ma sensu podawanie nazwy kolumny, ponieważ pełna informacja o każdej kolumnie jest
dostępna w opisie tabeli.
Tworzenie użytkowników i nadawanie uprawnień
Najczęściej spotykanym zadaniem administratora, oprócz tworzenia kopii zapasowych ważnych
danych, jest konfigurowanie uprawnień użytkowników. Począwszy od wersji 3.22 pakietu MySQL,
uprawnienia użytkownika mogą być modyfikowane za pomocą dwóch poleceń SQL: grant i
revoke. Te obydwa polecenia są uruchamiane z wiersza poleceń programu mysql.
grant
Polecenie grant używane w MySQL jest podobne do opisanego w standardzie SQL92, różni się
ono jednak kilkoma istotnymi szczegółami. Ogólna postać polecenia grant wygląda następująco:
grant uprawnienie on obiekt to użytkownik [hasło_użytkownika] [opcja];
Istnieje kilka uprawnień, które można nadać użytkownikowi:
Alter
Zmiana tabel i indeksów
Create
Tworzenie baz danych i tabel
Delete
Usuwanie danych z bazy
Drop
Usuwanie baz danych i tabel
Index
Obsługa indeksów
Insert
Dodawanie danych do bazy
Select
Pobieranie danych
Update
Modyfikacja danych
All
Wszystkie operacje
Istnieje także kilka uprawnień administratora bazy danych, ale nie dotyczą one omawianych tutaj
zagadnień.
Obiekt, którego dotyczą udzielane uprawnienia, jest identyfikowany jako:
databasename.tablename
Dozwolone jest stosowanie nazw wieloznacznych, w których  * oznacza  wszystko . Użycie
rick.* oznacza więc wszystkie tabele w bazie danych rick. Konsekwencją takiego sposobu
oznaczeń używanego przez MySQL jest możliwość nadania uprawnień do takiej bazy danych,
która nie istnieje. Może to się wydawać nieco dziwne, ale w praktyce może być całkiem użyteczne
(można np. nadać użytkownikowi uprawnienia do utworzenia konkretnej bazy danych).
Nazwa użytkownika podawana jako argument może dotyczyć użytkownika istniejącego albo
użytkownika nowego  i w tym wypadku zostanie on automatycznie utworzony. Użytkownicy
łączący się jako klienci z serwerem MySQL zawsze są identyfikowani jako username.host
(dotyczy to nawet użytkowników lokalnych, dla których nazwą komputera będzie localhost).
Specjalny symbol wieloznaczny  % oznacza  dowolny komputer . Polecenia grant można użyć
kilkakrotnie, ustalając uprawnienia dla określonych komputerów, których użytkownicy będą się
łączyć z serwerem. Można też nadawać uprawnienia od razu całej grupie komputerów z określonej
domeny:
rick@"%.docbox.co.uk"
W takim przypadku wymagane jest użycie cudzysłowu.
Użycie frazy  identified by powoduje ustawienie hasła dla danego użytkownika. Zazwyczaj
podczas tworzenia nowego użytkownika nadaje mu się hasło automatycznie, bowiem
pozostawienie go bez hasła zagraża bezpieczeństwu bazy danych.
with grant
Opcja with grant pozwala użytkownikowi przekazywać posiadane przez niego uprawnienia.
Normalnie nie używa się tej opcji, chyba że dotyczy ona tworzonego konta administratora bazy.
Dość już teorii, utwórzmy wreszcie konto użytkownika rick, który będzie mógł łączyć się z
dowolnego komputera, nadajmy mu hasło bar i pozwólmy mu na utworzenie bazy foo. Polecenie
mysql służące do tego celu wygląda następująco:
mysql> grant all on foo.* to rick@"%" identified by "bar";
Trzeba pamiętać o końcowym średniku wymaganym przy każdym poleceniu SQL.
W taki sposób został utworzony użytkownik rick z hasłem bar. Użytkownik ten będzie mógł
połączyć się z dowolnego komputera oraz utworzyć bazę foo i zarządzać nią. Dysponując takimi
uprawnieniami, rick może utworzyć bazę foo za pomocą zwykłego polecenia SQL create
database.
revoke, delete
Rozważając nadawanie uprawnień, musimy mieć także możliwość ich odbierania. W ogólnym
przypadku służy do tego polecenie revoke, które ma następującą składnię:
revoke uprawnienie on obiekt from użytkownik;
Jak widać, ma ono postać zbliżoną do polecenia grant, na przykład:
revoke insert on foo.* from rick@"%";
Istnieje tu jednak niewielkie odstępstwo, ponieważ nawet po zabraniu wszystkich uprawnień
danemu użytkownikowi zachowuje on niepożądaną możliwość łączenia się z bazą danych. Aby
całkowicie usunąć konto użytkownika, należy usunąć jego dane z tabeli użytkowników bazy
danych MySQL (polecenie delete) i wymusić na mysql ponowny odczyt uprawnień (polecenie
flush). Dokonuje się tego w następujący sposób:
mysql> use mysql
mysql> delete from user where User = "rick" and Host = "%";
mysql> flush privileges;
Hasła
Jeżeli zdarzy się, że zapomnimy o nadaniu hasła, to możemy je ustawić w dowolnym czasie. W
tym celu należy zalogować się do bazy mysql jako root i użyć następującego polecenia:
mysql> select host, user, password from user;
Powinniśmy wówczas otrzymać następującą listę:
+-----------+----------+------------------+
| host | user | password |
+-----------+----------+------------------+
| localhost| root | 67457e226a1a15bd |
| % | rick | 7c9e0a41222752fa |
| .% | foo | |
+-----------+----------+------------------+
2 rows in set (0.00 sec)
Załóżmy, że chcemy ustawić hasło bar dla użytkownika foo. Wpisujemy wówczas:
mysql> update user set password= password('bar') where user= 'foo';
Wyświetlając ponownie zawartość tabeli mysql.user, otrzymujemy:
mysql> select host, user, password from user;
+-----------+----------+------------------+
| host | user | password |
+-----------+----------+------------------+
| localhost| root | 67457e226a1a15bd |
| % | rick | 7c9e0a41222752fa |
| .% | foo | 7c9e0a41222752fa |
+-----------+----------+------------------+
2 rows in set (0.00 sec)
mysql>
Teraz możemy być pewni, że hasło użytkownika foo jest takie samo, jak wcześniej ustawione
hasło użytkownika rick, bowiem takie same są hasła zaszyfrowane.
Tworzenie bazy danych
Pobawimy się teraz bazą danych o nazwie rick. Najpierw jako użytkownik root bazy mysql
musimy nadać użytkownikowi rick uprawnienia do tworzenia nowej bazy:
mysql> grant all on rick.* to rick@% identified by "bar";
Użytkownik rick ma teraz wszystkie uprawnienia do bazy danych o nazwie rick przy
połączeniach z dowolnego komputera.
Zakończymy pracę z programem mysql i wywołamy go ponownie jako użytkownik rick.
mysql> quit
Bye
$ mysql -u rick
mysql> create database rick;
Podłączamy się do bazy rick:
mysql> use rick
Możemy teraz tworzyć dowolne tabele w tej bazie.
Jak pamiętamy z opisu bazy PostgreSQL podanego w poprzednim rozdziale, w kilku przykładach
była tam używana tabela children. Polecenia SQL tworzące tę tabelę są następujące:
create table children (
childno SERIAL,
fname VARCHAR,
age INTEGER
);
Jeżeli zechcemy użyć tego w MySQL, to napotkamy na pierwszą przeszkodę: MySQL nie
obsługuje słowa kluczowego SERIAL. Na szczęście można ją łatwo pokonać (patrz podrozdział
opisujący różnice między PostgreSQL i MySQL). Równoważnikiem powyższych poleceń SQL w
bazie MySQL jest:
create table children (
childno INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,
fname VARCHAR(30),
age INTEGER
);
Jak widać, można łatwo wprowadzić poprawki. Wprowadzanie danych do tabel odbywa się
podobnie jak w PostgreSQL, trzeba jedynie pamiętać o pomijaniu kolumny z autoinkrementacją.
insert into children(fname, age) values("Jenny", 14);
insert into children(fname, age) values("Andrew", 10);
W taki oto sposób można wstawiać dowolną liczbę wierszy.
Wspomaganie SQL w PostgreSQL i MySQL
W rozdziale 3. omówiliśmy podstawy SQL, koncentrując się głównie na bazie PostgreSQL.
Sposób obsługi SQL w bazie MySQL jest podobny. Pomimo kilku wspomnianych wyżej różnic
podstawowy zakres poleceń SQL jest w obydwu bazach taki sam i większość z nich będzie działać
na obydwu platformach. Należy pamiętać, że obydwa serwery są intensywnie rozwijane, więc lista
różnic może się w przyszłości drastycznie zmienić. Oczywiście, podane tutaj różnice nie są
jedynymi  w książce opisujemy tylko te, z którymi najczęściej można się spotkać w
codziennych zastosowaniach.
MySQL nie obsługuje obecnie podzapytań (zagnieżdżonych zapytań, ang. subqueries),
jest to planowane dopiero w przyszłych wydaniach. Jak widzieliśmy w PostgreSQL,
użycie podzapytań może być bardzo przydatne w niektórych sytuacjach. Jeżeli zależy
nam na przenośności kodu SQL, to na ogół można zmodyfikować go w taki sposób, by
nie trzeba było stosować podzapytań.
W przypadku MySQL ważna jest wielkość liter w nazwach tabel, w przeciwieństwie do
PostgreSQL, gdzie wielkość liter w nazwie tabeli nie ma znaczenia. Wynika to z tego, że
baza MySQL przechowuje tabele w postaci pojedynczych plików i korzysta z właściwości
systemu plików obowiązujących w danym systemie operacyjnym. Dlatego właśnie w
systemie Linux w nazwach tabel ważna jest wielkość liter. Zazwyczaj nie stanowi to
problemu, chociaż należy unikać różnicowania tabel tylko przez nazwę, w której użyto
liter równej wielkości. Zalecamy, aby w nazwach tabel używać tylko małych liter.
MySQL nie obsługuje typu danych SERIAL, co można jednak dosyć łatwo obejść.
Zamiast słowa kluczowego SERIAL należy użyć INTEGER AUTO_INCREMENT NOT
NULL PRIMARY KEY. Oznacza to wprawdzie potrzebę wpisania dłuższego tekstu, ale
działa prawie tak samo jak słowo SERIAL w PostgreSQL. Dodatkową zaletą takiej
modyfikacji jest również to, że usuwanie tabel MySQL zawierających tak określony typ
danych nie wymaga dodatkowej pracy przy modyfikacji sekwencji, jak w wypadku
PostgreSQL. Należy jednak pamiętać, że słowo kluczowe SERIAL jest powszechnie
używane w SQL.
W MySQL występują polecenia blokady:
lock tables tablename READ | WRITE [,tablename READ | WRITE ...];
unlock tables;
W PostgreSQL także istnieje polecenie blokujące tabelę, ale prawie nigdy nie powinno
ono być używane. Zamiast tego przy automatycznej modyfikacji danych w PostgreSQL
należy używać transakcji. Ponieważ MySQL nie obsługuje obecnie transakcji (patrz
niżej), polecenie lock jest w nim używane znacznie częściej.
Blokada tabeli dla odczytu zapobiega jakiejkolwiek modyfikacji tej tabeli. Blokada tabeli
dla zapisu zapobiega odczytowi lub zapisowi danych przez inne procesy, ale główny
wątek może je zarówno odczytywać, jak i zapisywać. Polecenia blokady nie mogą być
zagnieżdżane, ponieważ przed wykonaniem każdego nowego polecenia blokady usuwana
jest automatycznie blokada tabel obwiązująca w danym momencie.
MySQL nie obsługuje transakcji (ang. transactions). Jest to podstawowa różnica w
stosunku do PostgreSQL i zarazem różnica najtrudniejsza do pokonania. W przypadku
prostszych modyfikacji tabel można czasami próbować dwóch sposobów:
Po pierwsze, można modyfikować (update) tabele, podając wartości wszystkich pól
modyfikowanego wiersza. Jeżeli np. chcemy zmienić kwotę salda na koncie klienta z 3 na
4, a w tabeli występują kolumny zawierające imię, nazwisko, numer konta i sald, to
zamiast pisać:
update account set amount = 4 where accnum = 3013;
wstawiamy następujące polecenie:
update account set amount = 4 where
accnum=3013 and customerfn = "Bilbo" and
customerln = "Baggins" and amount = 3;
Dzięki temu, jeżeli jakieś pole (kolumna) w danym wierszu zostało zmodyfikowane,
zanim stwierdziliśmy, że wartość salda wynosi 3 i chcemy ją zmienić na 4, to nasza
modyfikacja nie uda się, ponieważ nie będzie można znalezć wiersza spełniającego
wymagane kryteria. Po sprawdzeniu przyczyny niepowodzenia i obsłużeniu błędu należy
podjąć właściwą decyzję o dalszym postępowaniu.
Po drugie, można użyć polecenia lock tables, blokując dostęp innych użytkowników
do modyfikowanej tabeli. Jest to prostsze, ale nie tak łatwe jak w przypadku transakcji, a
dodatkowo obniża wydajność bazy danych, gdy pracuje z nią kilku użytkowników i
wymagane są częste modyfikacje danych.
Są to najważniejsze różnice, z którymi można się spotkać, stosując zestaw poleceń SQL
opisywanych w tej książce.
Dostęp do danych MySQL z języka C
Podobnie jak baza PostgreSQL, baza MySQL może być dostępna z poziomu wielu języków
programowania. Autorzy wiedzą o następujących:
C
C++
Java
Perl
Python
REXX
Tcl
PHP
Istnieje także sterownik ODBC umożliwiający dostęp z aplikacji Microsoft Windows oraz
sterownik ODBC dla systemu Linux  można więc korzystać i z takiej metody dostępu do bazy
MySQL.
W czasie pisania tej książki istniały zastrzeżenia dotyczące bezpieczeństwa sterowników ODBC,
więc nie zalecamy korzystania z nich jako głównego sposobu dostępu do bazy.
W tym rozdziale zajmiemy się tylko interfejsem języka C, co wcale nie oznacza, że jest on lepszy
pod jakimś względem niż interfejsy w innych językach programowania. Po prostu, wokół języka C
koncentruje się cała książka. Interfejs programowy dla MySQL w języku C jest bardzo zwięzły i
podobny w wielu przypadkach do interfejsu libpq w PostgreSQL. Nie ma tu jednak
odpowiednika osadzonych poleceń SQL w kodzie C, które w przypadku PostgreSQL są dostępne
w postaci biblioteki ecpg.
Funkcje połączeniowe
Rozróżnia się dwie fazy połączenia z języka C do bazy MySQL:
Inicjacja struktury obsługującej połączenie (ang. connection handle structure).
Fizyczne utworzenie połączenia.
Do inicjacji połączenia służy funkcja mysql_init:
MYSQL *mysql_init(MYSQL *);
Zazwyczaj przekazuje się do tej funkcji wartość NULL, a zwracany jest wskaznik do zainicjowanej
struktury. Jeżeli będzie przekazana istniejąca struktura, to nastąpi jej ponowna inicjacja. W
przypadku wystąpienia błędu zwracana jest wartość NULL.
MySQL faktycznie udostępnia dwa sposoby łączenia się z bazą danych, ale funkcja
mysql_connect, którą można czasem zobaczyć w starszych programach, jest już przestarzała i
nie będziemy jej tutaj opisywać.
Mamy więc utworzoną i zainicjowaną strukturę, ale nie zapewniliśmy jeszcze parametrów
wymaganych do połączenia z bazą. Do ustawiania parametrów i faktycznego połączenia służy
funkcja mysql_real_connect:
MYSQL *mysql_real_connect(MYSQL *connection,
const char *server_host,
constchar *sql_user_name,
const char *sql_password,
const char *db_name,
unsigned int port_number,
const char *unix_cocket_name,
unsigned int flags);
Wskaznik połączenia musi wskazywać na strukturę zainicjowaną wcześniej za pomocą funkcji
mysql_init. Parametr server_host jest nazwą lub adresem IP komputera, na którym działa
serwer MySQL. Jeżeli serwer działa na komputerze lokalnym, to należy użyć nazwy localhost,
co pozwoli na optymalizacje takiego połączenia.
Parametry sql_user_name i sql_password są używane przy logowaniu do bazy danych. Jeśli
nazwa używana przy logowaniu jest równa NULL, to przyjmowany jest bieżący identyfikator
użytkownika. Jeśli jako hasło podana będzie wartość NULL, to logujący się użytkownik będzie
miał dostęp tylko do tych danych na serwerze, które nie wymagają użycia hasła. Hasło jest
przesyłane przez sieć w postaci zaszyfrowanej.
Numer portu (port_number) i nazwa gniazda uniksowego (unix_socket_name) powinny mieć
odpowiednio wartości 0 i NULL, chyba że istnieją jakieś specjalne powody wymuszające użycie
wartości niestandardowych. Domyślnie ustawiane są odpowiednie wartości.
Znaczniki (flags) to wartość tworzona z wzorców bitowych poddanych logicznej operacji OR,
umożliwiająca zmianę niektórych właściwości protokołu używanego podczas połączenia.
Prawdopodobnie tylko dwa z nich będą rzeczywiście potrzebne:
CLIENT_ODBC  znacznik ten powinien być ustawiony wówczas, gdy przy zdalnym
połączeniu z bazą danych będzie używany sterownik ODBC.
CLIENT_FOUND_ROWS  jest to znacznik działający dość specyficznie; aby go
zrozumieć, musimy nieco wybiec naprzód.
W rozdziale poświęconym PostgreSQL mówiliśmy, że można określić liczbę wierszy, na
które działały polecenia INSERT, UPDATE i DELETE. W przypadku polecenia UPDATE
działanie bazy MySQL troszeczkę różni się od działania PostgreSQL (i większości innych
baz głównego nurtu).
Jeśli PostgreSQL zwraca liczbę wierszy, na które zadziałało polecenie UPDATE, to
faktycznie jest ona równa tylko liczbie tych wierszy, które spełniają kryteria podane w
deklaracji WHERE. W przypadku MySQL jest to liczba zmienionych wierszy, która może
różnić się nieco od poprzedniej.
Załóżmy, że mamy tabelę zawierającą wpisy trojga dzieci o imieniu Ann, w wieku 3 lata,
4 lata i 5 lat. W bazie PostgreSQL polecenie takie jak niżej:
UPDATE age SET age = 3 WHERE name = 'Ann'
będzie sygnalizować działanie na trzy wiersze  jest to liczba dzieci o imieniu Ann.
Baza MySQL będzie sygnalizować dwa wiersze  czyli liczbę wierszy faktycznie
zmienionych. Przekazując do funkcji connect znacznik CLIENT_FOUND_ROWS, można
upodobnić w tym obszarze działanie MySQL do działania PostgreSQL
Inne, rzadziej używane znaczniki są opisane w dokumentacji.
Jeżeli połączenie się nie uda, to zostanie zwrócona wartość NULL. Aby poznać przyczynę błędu,
możemy użyć funkcji mysql_error opisanej w dalszej części rozdziału.
Do zamknięcia połączenia po zakończeniu pracy należy używać funkcji mysql_close
(zazwyczaj jej wywołanie umieszcza się na końcu programu):
void mysql_close(MYSQL *connection);
Takie wywołanie zamyka połączenie. Jeżeli struktura opisująca połączenie była utworzona za
pomocą mysql_init (dzięki przekazaniu wartości NULL w pierwotnym wywołaniu
mysql_init), to zostanie zwolniona, wskaznik do niej przestanie mieć sens i można będzie użyć
go ponownie.
Funkcją bardzo blisko związaną z funkcjami połączenia jest mysql_options (ponieważ można ją
wywołać tylko między funkcjami mysql_init i mysql_real_connect). Służy ona do
ustawiania opcji połączenia.
int mysql_options(MYSQL *connection, enum ustawiana_opcja,
const char *argument);
Ponieważ w jednym wywołaniu można ustawiać tylko jedną opcję, to trzeba wywoływać tę
funkcję tyle razy, aż wymagane opcje zostaną ustawione (pamiętając o umieszczeniu wywołań
między wywołaniami mysql_init i mysql_real_connect). Niektóre opcje wymagają
argumentu, który nie jest typu char i w tym przypadku trzeba zmienić typ wartości na const
char *. Dostępnych jest kilka opcji, ale tutaj szerzej omówimy tylko trzy najważniejsze. Pełna
lista opcji jest podana w podręczniku systemowym, w dokumentacji rozprowadzanej w pakiecie
MySQL (zwykle jest ona umieszczana w katalogu /usr/doc) oraz w podręczniku w formacie pdf,
który można pobrać ze strony macierzystej MySQL.
Opcja Typ argumentu Znaczenie
MYSQL_OPT_CONNECT_TIMEOUT const unsigned int *
Ograniczenie czasowe
połączenia w postaci liczby
sekund.
MYSQL_OPT_COMPRESS
Brak, trzeba użyć NULL Użycie kompresji podczas
połączenia sieciowego.
MYSQL_INIT_COMMAND const char *
Polecenie, jakie ma być
wysyłane za każdym razem,
gdy ustanawiane jest
połączenie.
W przypadku udanego ustawienia opcji zwracane jest zero. Ponieważ jedyną operacją
wykonywaną przez tę funkcję jest ustawianie znaczników w strukturze opisującej połączenie, to
błąd oznacza po prostu użycie niewłaściwej opcji.
Aby np. ustawić siedmiosekundowe ograniczenie czasowe dla połączenia, można użyć
następującego kodu:
unsigned int timeout = 7;
...
connection = mysql_init(NULL);
ret = mysql_options(connection, MYSQL_OPT_CONNECT_TIMEOUT, (const char
*)&timeout);
if (ret) {
/* Handle error */
...
}
connection = mysql_real_connect(connection...
Po zapoznaniu się z podstawowymi informacjami na temat połączeń z bazą MySQL spróbujemy
napisać krótki program testowy.
Program ten nazywa się connect1.c i będzie łączył się z serwerem bazy rick na komputerze
lokalnym jako użytkownik rick posługujący się hasłem bar.
#include
#include
#include "mysql.h"
int main(int argc, char *argv[]) {
MYSQL *conn_ptr
conn_ptr = mysql_init(NULL);
if (!conn_ptr) {
fprintf(stderr, "mysql_init failed\n");
return EXIT_FAILURE;
}
conn_ptr = mysql_real_connect(conn_ptr, "localhost", "rick", "bar",
"rick", 0, NULL, 0);
if (conn_ptr) {
printf("Connection success\n");
} else {
printf("Connection failed\n");
}
mysql_close(conn_ptr);
return EXIT_SUCCESS;
}
Program należy skompilować. Zależnie od sposobu instalacji bazy MySQL może być potrzebne
dopisanie ścieżek do dołączanych plików nagłówkowych i do bibliotek oraz konsolidacja z
modułem bibliotecznym mysqlclient. Przy instalacji z pakietów RPM wymagany wiersz
uruchamiający kompilator ma postać:
$ gcc -I/usr/include/mysql connect1.c -L/usr/mysql -lmysqlclient -o connect1
Po uruchomieniu programu powinniśmy ujrzeć komunikat mówiący o udanym połączeniu z bazą:
$ ./connect1
Connection success
$
Widzimy więc, że nawiązywanie połączeń z bazą danych MySQL jest bardzo proste.
Obsługa błędów
Zanim przejdziemy do bardziej przydatnych programów, musimy zapoznać się ze sposobem
obsługi błędów stosowanym w MySQL. Wszystkie błędy są sygnalizowane za pomocą zwracanego
kodu, a ich szczegóły są zawarte w strukturze obsługującej połączenie. Trzeba poznać tylko dwie
następujące funkcje:
unsigned int mysql_errno(MYSQL *connection);
i
char *mysql_error(MYSQL *connection);
Jeżeli jakaś funkcja mysql zwraca liczbę całkowitą będącą kodem błędu (ogólnie dotyczy to
każdej niezerowej wartości), to można ten kod uzyskać, korzystając z funkcji mysql_errno
odwołującej się do struktury połączenia. Jeśli nie był ustawiony żaden kod błędu, to zwracane jest
zero. Kod ten jest modyfikowany przy każdym odwołaniu do biblioteki, więc można pobierać
tylko kod błędu dotyczący ostatniego polecenia (wyjątkiem są tu oczywiście wywołania samych
funkcji do obsługi błędów, które nie modyfikują kodu błędu).
Kody błędów są zdefiniowane w pliku errmsg.h lub mysql_error.h, które znajdują się w
specyficznym dla MySQL katalogu include przeznaczonym na pliki nagłówkowe. Pierwszy z
tych plików zawiera definicje kodów błędu związanego z klientami (np. utrata połączenia z bazą),
w drugim zaś znajdują się definicje błędów serwera (np. przekazanie niewłaściwego polecenia).
Jeżeli potrzebne są tekstowe komunikaty o błędach, to można wywołać funkcję mysql_error.
Komunikat przechowywany jest w wewnętrznym statycznym obszarze pamięci, zatem przed
wykonaniem jakichś operacji trzeba go stamtąd skopiować w inne miejsce.
Po dodaniu podstawowej obsługi błędów do naszego programu testowego możemy zobaczyć, jak
działa to w rzeczywistości. Niektórzy zapewne zauważyli, że możemy napotkać tu pewien
problem. Jeżeli funkcja mysql_real_connect zwróci przy błędzie wskaznik połączenia o
wartości NULL, to w jaki sposób uzyskamy kod błędu? Odpowiedzią jest przyporządkowanie
zmiennej do wskaznika połączenia, która jest dostępna nawet wówczas, gdy wywołanie funkcji
mysql_real_connect się nie uda.
W podanym niżej programie connect2.c pokazujemy sposób wykorzystania struktury
połączenia, która nie jest tworzona dynamicznie, oraz podstawową obsługę błędów. Zmiany w
stosunku do poprzedniego programu zostały wyróżnione:
#include
#include
#include "mysql.h"
int main(int argc, char *argv[]) {
MYSQL my_connection;
mysql_init(&my_connection);
if (mysql_real_connect(&my_connection, "localhost", "rick",
"bar", "rick", 0, NULL, 0)) {
printf("Connection success\n");
mysql_close(&my_connection);
} else {
fprintf(stderr, "Connection failed\n");
if (mysql_errno(&my_connection)) {
fprintf)stderr, "Connection error %d: %s\n",
mysql_errno(&my_connection), mysql_error(&my_connection));
}
}
return EXIT_SUCCESS;
}
W rzeczywistości rozwiązaliśmy nasz problem zupełnie łatwo, unikając  nadpisywania
wskaznika połączenia przez zwracany wynik w wypadku nieudanego wywołania
mysql_real_connect. Niezależnie od tego, jest to przykład innego sposobu korzystania ze
struktury połączenia. Jeżeli wymusimy powstanie błędu (np. używając niewłaściwego hasła), to
otrzymamy kod błędu i tekst komunikatu o błędzie, czyli to wszystko, czego oczekujemy od
interaktywnego narzędzia mysql.
Wykonywanie poleceń SQL
Mamy już możliwość połączenia się z bazą i wiemy, jak obsługiwać błędy, zatem nadeszła pora na
zajęcie się czymś, co można będzie wykorzystać w naszej bazie. Dowolne polecenie SQL jest
uruchamiane za pomocą funkcji mysql_query:
int mysql_query(MYSQL *connection, const char *query)
Jak widać, wywołanie to jest nadzwyczaj proste. Wymaga ono podania wskaznika struktury
połączenia i napisu zawierającego polecenie SQL, które ma być wykonane. W odróżnieniu od
wiersza poleceń nie należy tu używać średnika kończącego polecenie. Jeżeli uda się wykonać
polecenie, to zostanie zwrócone zero. W specjalnym przypadku, gdy wymagane jest dołączenie
danych binarnych, można użyć pokrewnej funkcji o nazwie mysql_real_query, ale w tym
rozdziale nie będziemy jej omawiać.
Polecenia SQL, które nie zwracają żadnych danych
Najpierw zapoznamy się z poleceniami UPDATE, DELETE i INSERT. Ponieważ nie zwracają one
żadnych danych z bazy, to łatwiej będzie się nimi posługiwać.
Następną ważną funkcją, którą tutaj omówimy, jest funkcja sprawdzająca liczbę wierszy
poddanych działaniu danego polecenia:
my_ulonglong mysql_affected_rows(MYSQL *connection);
Prawdopodobnie najbardziej oczywistą rzeczą jest jej nietypowy wynik. Ze względu na
przenośność kodu zwracany wynik ma specjalny typ bez znaku. Jeżeli chcemy go wykorzystać w
funkcji printf, to należy zmienić jego typ na unsigned long i użyć formatu %lu. Funkcja ta
zwraca liczbę wierszy poddanych działaniu poleceń UPDATE, INSERT lub DELETE przekazanych
do bazy za pomocą funkcji mysql_query.
W odróżnieniu od pozostałych funkcji mysql_, zerowy wynik oznacza, że nie zostały wybrane
żadne wiersze zaś wynik dodatni oznacza na ogół faktyczną liczbę wierszy, na które działały
polecenia SQL.
Jak już wspomnieliśmy wcześniej, podczas korzystania z funkcji mysql_affected_rows
pojawiają się pewne niespodziewane wyniki. Spójrzmy wiec najpierw na liczbę wierszy
poddawaną działaniu polecenia INSERT, która zachowuje się tak jak oczekiwaliśmy. Po dopisaniu
wyróżnionego niżej kodu do programu connect2.c nazwiemy go insert1.c:
#include
#include
#include "mysql.h"
int main(int argc, char *argv[]) {
MYSQL my_connection;
int res;
mysql_init(&my_connection);
if (mysql_real_connect(&my_connection, "localhost",
"rick", "bar", "rick", 0, NULL, 0)) {
printf("Connection success\n");
res = mysql_query(&my_connection, "INSERT INTO children(fname, age)
VALUES('Ann', 3)");
if (!res) {
printf("Inserted %lu rows\n",
(unsigned long)mysql_affected_rows(&my_connection));
} else {
fprintf(stderr,"Insert error %d: %s\n", mysql_errno(&my_connection),
mysql_error(&my_connection));
}
my_close(&my_connection);
} else {
fprintf(stderr, "Connection failed\n");
if (mysql_errno(&my_connection)) {
fprintf(stderr, "Connection error %d: %s\n",
mysql_errno(&my_connection),
mysql_error(&my_connection));
}
}
return EXIT_SUCCESS;
}
Tak jak tego oczekiwaliśmy, liczba wierszy podana przez program wynosi jeden.
Zmienimy teraz kod programu, zastępując część realizującą polecenie  INSERT następującym
fragmentem:
mysql_errno(&my_connection), mysql_error(&my_connection));
}
}
res = mysql_query(&my_connection, "UPDATE children SET AGE = 4
WHERE fname = 'Ann'");
if (!res) {
printf("Updated %lu rows\n",
(unsigned long)mysql_affected_rows(&my_connection));
} else {
fprintf(stderr, "Update error %d: %s\n", mysql_errno(&my_connection),
mysql_error(&my_connection));
}
Nowy program nazwiemy update1.c.
Załóżmy teraz, że w tabeli children są następujące dane:
+---------+--------+------+
| childno | fname | age |
+---------+--------+------+
| 1| Jenny | 14|
| 2| Andrew | 10|
| 3| Gavin | 4|
| 4| Duncan | 2|
| 5| Emma | 0|
| 6| Alex | 11|
| 7| Adrian | 5|
| 8| Ann | 3|
| 9| Ann | 4|
| 10| Ann | 3|
| 11| Ann | 4|
+---------+--------+------+
Po uruchomieniu programu update1 spodziewamy się, że zwrócona liczba wierszy będzie
wynosić 4, ale program sygnalizuje 2, ponieważ w rzeczywistości zmienił tylko dwa wiersze,
niezależnie od tego, że deklaracja WHERE wykrywa cztery. Jeśli funkcja mysql_affected_rows
ma zwracać w takim przypadku 4, czyli wynik zgodny z innymi bazami danych, to do funkcji
mysql_real_connect należy przekazać znacznik CLIENT_FOUND_ROWS, podobnie jak w
programie update2.c:
if (mysql_real_connect(&my_connection, "localhost"'
"rick", "bar", "rick", 0, NULL, CLIENT_FOUND_ROWS)) {
Jeżeli przywrócimy poprzednie dane w bazie i uruchomimy tak zmodyfikowany program, to
otrzymamy liczbę wierszy równą 4.
Funkcja mysql_affected_rows charakteryzuje się jeszcze jednym udziwnieniem, które objawia
się przy usuwaniu danych z bazy. Jeżeli usuniemy dane spełniające kryterium podane w deklaracji
WHERE, to mysql_affected_rows zawróci liczbę faktycznie usuniętych wierszy  tak jak tego
oczekujemy. Jeżeli jednak nie ma deklaracji WHERE i z tego powodu zostaną usunięte wszystkie
wiersze tabeli, to funkcja ta zwraca wartość zerową. Wynika to z tego, że w takich przypadkach w
wyniku optymalizacji działania bazy usuwana jest cała tabela. Na takie zachowanie nie ma
wpływu znacznik opcji CLIENT_FOUND_ROWS.
Polecenia zwracające dane
Nadszedł teraz czas na zajęcie się najczęściej używanym poleceniem SQL, a mianowicie
poleceniem SELECT, które służy do pobierania danych z bazy.
MySQL obsługuje również polecenia SHOW, DESCRIBE oraz EXPLAIN, które również zwracają
wyniki, ale nie będziemy ich tutaj omawiać. Jak zwykle, ich szczegółowy opis można znalezć w
podręczniku.
Jak pamiętamy z opisu PostgreSQL, możemy tam pobrać dane za pomocą polecenia SELECT,
umieszczając je w wywołaniu pqexec (wtedy wszystkie dane zostaną pobrane w jednej porcji),
albo użyć kursora do pobierania danych wiersz po wierszu (co nie dopuszcza do przeciążenia sieci
lub programu klienta).
MySQL dysponuje prawie takimi samymi możliwościami wyboru metod pobierania danych, mimo
że pobieranie wiersz po wierszu nie jest tu nazwane  operacją z użyciem kursora . Interfejs (API)
opisujący pobieranie danych za pomocą tych dwóch metod niewiele się różni, zatem łatwiej jest
przełączyć się z użycia jednej metody na użycie drugiej.
Funkcje umożliwiające jednorazowe pobranie wszystkich danych
Za pomocą polecenia SELECT (lub innych poleceń zwracających dane) można pobrać wszystkie
dane od razu używając jednego wywołania funkcji mysql_store_result:
MYSQL_RES *mysql_store_result(MYSQL *connection);
Aby zachować pobrane dane w tzw. zestawie wyników, funkcja ta musi być wywołana po funkcji
mysql_query. Pobieranie danych z serwera i ich zachowanie w programie klienta następuje
natychmiastowo. Funkcja zwraca wskaznik do nie omawianej jeszcze struktury nazywanej
zestawem wyników. Jeżeli wykonanie polecenia się nie uda, to zwracany jest wskaznik o wartości
NULL.
[[[ramka]]]
Podobnie jak w bazie PostgreSQL, tutaj zwrócenie wskaznika NULL także oznacza, że wystąpił
błąd i jest to coś innego niż brak danych. Nawet wówczas, gdy zwrócona wartość nie jest równa
NULL, to nie oznacza to, że dane będą dostępne.
[[[koniec ramki]]]
Jeżeli wartość NULL nie była zwrócona, to można wywołać funkcję mysql_num_rows i pobrać
liczbę wierszy faktycznie poddanych działaniu polecenia SQL (może ona być równa zeru).
my_ulonglong mysql_num_rows(MYSQL_RES *result);
Funkcja ta odnosi się do struktury wynikowej zwróconej przez mysql_store_result i zwraca
liczbę wierszy w zestawie wynikowym, która może być równa zeru. Jeżeli wywołanie funkcji
mysql_store_result odbyło się pomyślnie, to wywołanie mysql_num_rows także się uda.
Kombinacja funkcji mysql_store_result i mysql_num_rows stanowi prosty i intuicyjny
sposób pobierania danych. Po udanym wywołaniu mysql_store_result wszystkie dane
otrzymane w wyniku zapytania będą zachowane w programie klienta i można je pobierać ze
struktury wynikowej bez narażania się na ryzyko utraty połączenia z bazą danych  dane są już
przechowywane lokalnie. Można także bezpośrednio określić liczbę pobranych wierszy, co
ułatwia programowanie. Jak wspomnieliśmy wcześniej, wszystkie pobrane dane są przesyłane do
programu klienta w postaci jednej porcji. Przy zestawach wynikowych o dużych rozmiarach może
to wymagać zaangażowania znacznej części zasobów serwera, sieci i klienta. Dlatego właśnie,
pracując z dużymi zestawami danych, lepiej pobierać je mniejszymi porcjami. Wkrótce omówimy
taki sposób pracy wykorzystujący funkcje mysql_use_result.
Po pobraniu wszystkich danych do programu klienta możemy pobierać je za pomocą funkcji
mysql_fetch_row, a także przemieszczać się w zestawie wynikowym za pomocą funkcji
mysql_data_seek, mysql_row_seek i mysql_row_tell. Zanim omówimy pobieranie danych
porcjami, spójrzmy na opis tych funkcji.
mysql_fetch_row
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
Funkcja ta odnosi się do struktury wynikowej otrzymanej w wyniku wywołania
mysql_store_result i pobiera z niej pojedynczy wiersz, zwracając dane w postaci utworzonej
przez siebie struktury zgodnej ze strukturą wiersza. Jeżeli nie ma danych lub wystąpi błąd, to
zostanie zwrócona wartość NULL. Przetwarzanie danych w strukturze wiersza omówimy pózniej.
mysql_data_seek
void mysql_data_seek(MYSQL_RES *result, my_ulonglong offset);
Ta funkcja pozwala na dokonywanie przeskoków w zestawie wynikowym, ustawiając numer
wiersza zwracanego w następnym wywołaniu mysql_fetch_row. Wartość offset jest numerem
wiersza i musi się mieścić w przedziale od zera do wartości o jeden mniejszej niż liczba wierszy w
zestawie wynikowym. Użycie wartości zerowej spowoduje, że w następnym wywołaniu
mysql_fetch_row zostanie zwrócony pierwszy wiersz.
mysql_row_tell, mysql_row_seek
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result);
Ta funkcja zwraca wartość offsetu odniesioną do bieżącej pozycji w zestawie wynikowym. Nie
jest to numer wiersza, więc nie można tej wartości użyć w funkcji mysql_data_seek. Można
natomiast skorzystać z następującego wywołania:
MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset);
Spowoduje ono zmianę bieżącej pozycji w zestawie wynikowym i zwrócenie pozycji poprzedniej.
Powyższa para funkcji przydaje się czasem przy dokonywaniu przeskoków między znanymi
miejscami w zestawie wynikowym. Należy jednak pamiętać o tym, że nigdy nie wolno pomylić
wartości offsetu zwracanej przez funkcje mysql_row_tell i mysql_row_seek z numerem
wiersza zwracanym przez mysql_data_seek. Wartości te nie mogą być używane zamiennie,
ponieważ wyniki uzyskiwane w takich przypadkach będą błędne.
mysql_free_result
Przed użyciem tych pomocniczych funkcji omówimy ostatnią z nich, czyli mysql_free_result.
void mysql_free_result(MYSQL_RES *result);
Po zakończeniu pracy z zestawem wynikowym zawsze trzeba wywołać tę funkcję, pozwalając w
ten sposób bibliotece MySQL na zwolnienie pamięci zajmowanej przez utworzone obiekty.
Pobieranie danych
Możemy już utworzyć pierwszy program pobierający dane z bazy MySQL. Mamy zamiar wybrać
zawartość wszystkich wierszy, w których pole age ma wartość większą niż 5. Niestety, nie
potrafimy jeszcze przetwarzać tych danych, dlatego posłużymy się pętlą pobierającą je kolejno.
Program nazywa się select1.c:
#include
#include
#include "mysql.h"
MYSQL my_connection;
MYSQL_RES *res_ptr;
MYSQL_ROW sqlrow;
int main(int argc, char *argv[]) {
int res;
mysql_init(&my_connection);
if (mysql_real_connect(&my_connection, "localhost", "rick",
"bar", "rick", 0, NULL, 0)) {
printf("Connection success\n");
res = mysql_query(&my_connection, "SELECT childno, fname,
age FROM children WHERE age > 5");
if (res) {
printf("SELECT error: %s\n", mysql_error(&my_connection));
} else {
res_ptr = mysql_store_result(&my_connection);
if (res_ptr) {
printf("Retrived %lu rows\n", (unsigned
long)mysql_num_rows(res_ptr));
while ((sqlrow = mysql_fetch_row(res_ptr)) {
printf("Fetched data...\n");
}
if (mysql_errno(&my_connection)) {
fprintf(stderr, "Retrive error: %s\n",
mysql_error(&my_connection));
}
}
mysql_free_result(res_ptr);
}
mysql_close(&my_connection);
} else {
fprintf(stderr, "Connection failed\n");
if (mysql_errno(&my_connection)) {
fprintf(stderr, "Connection error %d: %s\n",
mysql_errno(&my_connection), mysql_error(&my_connection));
}
}
return EXIT_SUCCESS;
}
Wyróżniono tu ważny fragment kodu, który jest odpowiedzialny za pobranie zestawu wyników i
przejście w pętli przez pobrane dane.
Pobieranie danych po jednym wierszu naraz
Aby pobierać dane wiersz po wierszu, a nie pobierać ich w całości i przechowywać ich w
programie klienta, musimy zastąpić wywołanie mysql_store_result przez wywołanie funkcji
mysql_use_result:
MYSQL_RES *mysql_use_result(MYSQL *connection);
Ta funkcja również odnosi się do obiektu definiującego połączenie i zwraca wskaznik do zestawu
wynikowego lub NULL w wypadku wystąpienia błędu. Wskaznik ten jest zwracany podobnie jak w
funkcji mysql_store_result, natomiast nie następuje faktyczne pobranie danych do zestawu
wynikowego, a tylko jego inicjacja i ustawienie w gotowości do przyjęcia danych.
[[[ramka]]]
Aby naprawdę pobrać dane, trzeba powtarzać wywołania mysql_fetch_row tak jak poprzednio, aż
do pobrania wszystkich danych. Jeżeli przy wywołaniu funkcji mysql_use_result wystąpi błąd, to
następujące po tym pobieranie danych zostanie zakłócone.
[[[koniec ramki]]]
Co się stanie, jeżeli użyjemy funkcji mysql_use_result? Potencjalnie zyskujemy większą
swobodę, ponieważ mniej obciążamy sieć i program klienta, ale istnieje także wada takiego
sposobu pobierania danych: przy wywołaniu mysql_use_result nie możemy skorzystać z
funkcji mysql_num_rows, mysql_data_seek, mysql_row_seek i mysql_row_tell. W
rzeczywistości nie jest to do końca prawdą, ponieważ możemy wywołać mysql_num_rows, lecz
nie otrzymamy wtedy liczby dostępnych wierszy aż do momentu pobrania ostatniego z nich za
pomocą funkcji mysql_fetch_result. Faktycznie więc nie przyda się to do niczego.
Powiększamy także opóznienie miedzy żądaniami pobrania wiersza, ponieważ za każdym razem
musi on być przesyłany przez sieć. Dodatkowy problem może stwarzać możliwość wystąpienia
błędu sieci podczas utrzymywania otwartego połączenia z bazą danych, zanim wszystkie dane
zostaną pobrane. Nie będziemy wówczas mieli rzeczywistego dostępu do danych, ponieważ nie są
one przechowywane lokalnie.
Mamy także duże korzyści: wyrównujemy obciążenie sieci i znacznie zmniejszamy potencjalnie
duże zapotrzebowanie na pamięć (ang. storage overhead) w programie klienta. Przy dużych
zestawach danych prawie zawsze preferuje się pobieranie wiersza po wierszu za pomocą funkcji
mysql_use_result.
Zmiana w programie select1.c polegająca na użyciu funkcji mysql_use_result jest łatwa do
przeprowadzenia. Otrzymujemy w ten sposób program select2.c, w którym zaznaczyliśmy
zmienione wiersze:
if (res) {
printf("SELECT error: %s\n", mysql_error(&my_connection));
} else {
res_ptr = mysql_use_result(&my_connection);
if (res_ptr) {
while ((sqlrow = mysql_fetch_row(res_ptr))) {
printf("Fetched data...\n");
}
if (mysql_errno(&my_connection)) {
printf("Retrieve error: %s\n", mysql_error(&my_connection));
}
}
mysql_free_result(res_ptr);
}
Zwróćmy uwagę na to, że nie jest tu znana liczba wierszy pobranych bezpośrednio po otrzymaniu
wyniku. Oprócz tego użyto tutaj wcześniej stosowanej metody obsługi błędów polegającej na
sprawdzaniu wartości zwracanej przez mysql_errno(&my_connection). Pisząc program
wykorzystujący funkcję mysql_store_result i mając zamiar zmienić ją w przyszłości na
funkcję mysql_use_result, lepiej od razu wprowadzić stosowne zmiany. Dla pewności można
także sprawdzać wartości zwracane przez wszystkie funkcje.
Przetwarzanie zwróconych danych
Samo pobieranie danych nie ma większego sensu, jeżeli nie będziemy z nimi niczego robić.
Podobnie jak w bazie PostgreSQL, tutaj również rozróżnia się dwa rodzaje danych:
Rzeczywista informacja pobrana z bazy danych.
Informacja na temat danych, czyli tzw. metadane (ang. metadata).
Najpierw zobaczymy, jak można odtwarzać i wyświetlać dane, a następnie zajmiemy się
sposobami określania nazw kolumn i innych informacji o danych.
W nowszych wersjach MySQL dostępna jest funkcja mysql_field_client, która odnosi się do
obiektu połączenia i zwraca liczbę pól w zestawie wynikowym:
unsigned int mysql_field_count(MYSQL *connection);
Ta funkcja może być także wykorzystana do bardziej ogólnego przetwarzania, np. do określania,
czy przy wywołaniu mysql_store_result wystąpił błąd. Jeżeli mysql_store_result zwróci
NULL, ale funkcja mysql_field_count zwraca liczbę większą od zera, to możemy być pewni, że
w zestawie wynikowym są jakieś dane, chociaż wystąpił błąd przy ich odtwarzaniu. Jeżeli jednak
funkcja mysql_field_count zwraca zero, to nie ma żadnych kolumn do pobrania, a zatem
próba zachowania wyniku nie uda się.
Prawdopodobnie taka metoda będzie częściej stosowana wówczas, gdy polecenie SQL nie jest
znane z góry albo gdy programista chce utworzyć moduł przeznaczony do całkowicie ogólnego
przetwarzania zapytań.
W programach wykorzystujących starsze wersje MySQL można spotkać wywołania funkcji
mysql_num_fields. Posługują się one wskaznikami struktury połączenia lub struktury wyników
i zwracają liczbę wierszy.
W nowszych programach należy w zasadzie używać funkcji mysql_field_count, chyba że
program ma współpracować ze starszą wersją MySQL.
Jeżeli chcemy po prostu otrzymać informację w postaci niesformatowanego tekstu i wiemy, w jaki
sposób bezpośrednio wyświetlić dane, to możemy wykorzystać strukturę MYSQL_ROW zwracaną
przez funkcję mysql_fetch_row. Dodamy więc do naszego programu select2.c prostą
funkcję display_row wyświetlającą dane.
Zwróćmy uwagę na to, że w celu uproszczenia programu struktura połączenia, struktura wyników
i informacja zwracana przez funkcję mysql_fetch_row mają charakter globalny. W kodzie
produkcyjnym nie zalecamy takiego podejścia.
Oto jak wygląda nasza prosta funkcja wyświetlająca dane:
void display_row() {
unsigned int field_count;
field_count = 0;
while (field_count < mysql_field_count(&my_connection)) {
printf("%s ", sqlrow[field_count]};
field_count++;
}
printf("\n");
}
Dodamy ją do programu select.c oraz dołączymy jej deklarację i wywołanie:
void display_row();
intmsin(int argc, char *argv[]) {
int res;
mysql_init(&mysql_real_connect(&my_connection, "localhost", "rick",
"bar, "rick", 0, NULL, 0)) {
printf("Connection success\n");
res = mysql_query(&my_connection, "SELECT childno, fname,
age FROM children WHERE age > 5");
if (res) {
printf("SELECT error: %s\n", mysql_error(&my_connection));
} else {
res_ptr = mysql_use_result(&my_connection);
if (res_ptr) {
while ((sqlrow = mysql_fetch_row(res_ptr))) {
printf("Fetched data...\n");
display_row();
}
Nowy program nazwiemy select3.c. Następnie skompilujemy go i uruchomimy:
$ gcc -I/usr/include/mysql select3.c -L/usr/lib/mysql -lmysqlclient -o
select3
$ ./select3
Connection success
Fetched data...
1 Jenny 14
Fetched data...
2 Andrew 10
Fetched data...
6 Alex 11
$
Jak widać, wszystko działa prawidłowo, chociaż formatowanie wyników jest bardzo ograniczone.
Nie braliśmy pod uwagę możliwych wartości NULL w zwracanych wynikach. Jeżeli trzeba np.
przedstawić dane w postaci tabelarycznej, to należy uzyskać zarówno dane, jak i informacje
dodatkowe o tych danych w nieco innej postaci. W jaki sposób mamy to zrobić?
Zamiast korzystania z obiektu będącego wierszem (zdefiniowanego jako char **), który jest
zwracany przez mysql_fetch_row, możemy pobierać informację w postaci pojedynczych pól do
struktury zwierającej zarówno dane, jak i metadane. Wykonuje to funkcja mysql_fetch_field:
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);
Należy powtarzać wywołania tej funkcji wielokrotnie, przechodząc po kolei przez wszystkie pola.
Jeżeli nie będzie już żadnych pól do przetworzenia, to funkcja zwróci wartość NULL. Wskaznik
zwróconej struktury pola może być także wykorzystany do uzyskania różnych informacji o
kolumnie przechowywanej w tej strukturze. Definicja tej struktury jest podana w pliku mysql.h:
Pole w strukturze MYSQL_FIELD Znaczenie
char *name;
Nazwa kolumny jako napis.
char *table;
Nazwa tabeli, z której pochodzi kolumna. Przydaje się,
gdy dane są pobierane z większej liczby tabel. Należy
pamiętać, że dla wyliczanych wartości (np. MAX) jako
nazwę tabeli otrzymujemy pusty napis.
char *def;
Jeśli wywołamy funkcję mysql_list_fields (która nie
jest tu opisana), to pole def będzie zawierało wartość
domyślną dla kolumny.
enum enum_field_types type;
Typ wartości w kolumnie. Patrz niżej.
unsigned int length;
Szerokość kolumny określona przy definiowaniu tabeli.
unsigned int max_length;
Jeśli użyjemy funkcji mysql_store_result, to pole
max_length będzie zawierać rzeczywistą znalezioną
największą szerokość kolumny. Pole to nie jest ustawiane
przy wywołaniu mysql_use_result.
unsigned int flags;
Znaczniki informujące o definicji kolumny, a nie o
faktycznie znalezionych danych. Najczęściej są to:
NOT_NULL_FLAG, PRI_KEY_FLAG, UNSIGNED_FLAG,
AUTO_INCREMENT_FLAG, BINARY_FLAG. Pełny spis
znajduje się w dokumentacji.
unsigned int decimals;
Liczba miejsc dziesiętnych, ważna tylko w polach
liczbowych.
Zakres typów kolumn jest bardzo szeroki. Pełna lista jest podana w pliku mysql_com.h i w
dokumentacji. Najczęściej spotyka się następujące typy:
FIELD_TYPE_DECIMAL
FIELD_TYPE_LONG
FIELD_TYPE_STRING
FIELD_TYPE_VAR_STRING
Jednym ze szczególnie przydatnych makropoleceń jest IS_NUM, zwracające wartość TRUE, jeżeli
dane pole jest typu liczbowego, na przykład:
if (IS_NUM(mysql_field_ptr->type)) printf("Numeric type field\n");
Przed modyfikacją naszego programu musimy jeszcze wspomnieć o pewnej dodatkowej funkcji:
MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,
MYSQL_FIELD_OFSET offset);
Funkcja ta umożliwia zmianę bieżącego numeru pola, który jest powiększany automatycznie przy
każdym wywołaniu mysql_fetch_field, a po przekazaniu offsetu równego zero  skok do
pierwszej kolumny w wyniku. Funkcja zwraca poprzednią wartość offsetu.
Mamy już wystarczająco dużo informacji do wprowadzenia takich zmian w przykładowym
programie wybierającym dane, aby informacja o kolumnie stała się dostępna. Dzięki temu można
będzie wyświetlać dane w bardziej przejrzysty sposób.
Nasz zmodyfikowany program nazywa się select4.c. Pokazujemy go tutaj w całości, aby
czytelnik dysponował pełny przykładem. W programie nie próbujemy analizować typów kolumn,
pokazując jedynie obowiązujące zasady.
#include
#include
#include "mysql.h"
MYSQL my_connection;
MYSQL_RES *res_ptr;
MYSQL_ROW sqlrow;
void display_header();
void display_row();
int main(int argc, char *argv[]) {
int res;
int first_row = 1;
mysql_init(&my_connection);
if (mysql_real_connect(&my_connection, "localhost", "rick",
"bar", "rick", 0, NULL, 0)) {
printf("Connection success\n");
res = mysql_query(&my_connection, "SELECT childno, fname,
age FROM children WHERE age > 5");
if (res) {
fprintf(stderr, "SELECT error: %s\n", mysql_error(&my_connection));
} else {
res_ptr = mysql_use_result(&my_connection);
if (res_ptr) {
display_header();
while ((sqlrow = mysql_fetch_row(res_ptr))) {
if (first_row) {
display_header();
first_row = 0;
}
display_row();
}
if (mysql_errno(&my_connection)) {
fprintf(stderr, "Retrieve error: %s\n",
mysql_error(&my_connection));
}
}
mysql_free_result(res_ptr);
}
mysql_close(&my_connection);
} else {
fprintf(stderr, "Connection failed\n");
if (mysql_errno(&my_connection)) {
fprintf(stderr, "Connection error %d: %s\n",
mysql_errno(&my_connection),
mysql_error(&my_connection));
}
}
return EXIT_SUCCESS;
}
void display_header() {
MYSQL_FIELD *field_ptr;
printf("Column details:\n");
while ((field_ptr = mysql_fetch_field(res_ptr)) != NULL) {
printf("\t Name: %s\n", field_ptr->name);
printf("\t Type: ");
if (IS_NUM(field_ptr->type)) {
printf("Numeric field\n");
} else {
switch(field_ptr->type) {
case FIELD_TYPE_VAR_STRING:
printf("VARCHAR\n");
break;
case FIELD_TYPE_LONG:
printf("LONG\n");
break;
default:
printf("Type is %d, check in mysql_com.h\n", field_ptr->type);
} /* switch */
} /* else */
printf("\t Max width %d\n", field_ptr->length);
if (field_ptr->flags & AUTO_INCREMENT_FLAG)
printf("\t Auto increments\n");
printf("\n");
} /* while */
}
void display_row() {
unsigned int field_count;
field_count = 0;
while (field_count < mysql_field_count(&my_connection)) {
if (sqlrow[field_count]) printf("%s ", sqlrow[field_count]);
else printf("NULL");
field_count++;
}
printf("\n");
}
Po kompilacji uruchamiamy program i otrzymujemy:
$ ./select4
Connection success
Column details:
Name: childno
Type: Numeric field
Max width 11
Auto increments
Name: fname
Type: VARCHAR
Max width 30
Name: age
Type: Numeric field
Max width 11
Column details:
1 Jenny 14
2 Andrew 10
6 Alex 11
$
Nie wygląda to wprawdzie w pełni doskonale, ale spełnia swoją rolę informacyjną.
Istnieją także inne funkcje umożliwiające pobieranie tablic pól i przeskakiwanie między
kolumnami. W ogólnym przypadku wszystkie wymagane funkcje są zawarte w naszym
przykładzie, natomiast zainteresowani czytelnicy mogą dowiedzieć się więcej z podręcznika
opisującego MySQL.
Funkcje dodatkowe
Istnieje kilka funkcji API, których nie można zaklasyfikować do omawianych dotychczas
kategorii. Są one jednak przydatne i dlatego warto je poznać. Jeżeli tylko jest to możliwe, to
wszelkie operacje na bazie danych powinny się odbywać za pośrednictwem interfejsu
mysql_query. Istnieje np. w API funkcja mysql_create_db, która służy do tworzenia bazy
danych, ale prostsze jest użycie polecenia CREATE DATABASE w połączeniu z mysql_query,
ponieważ do utworzenia bazy wystarcza wtedy jedynie znajomość SQL, a nie specjalizowanych
wywołań API.
Dodatkowe funkcje API, które bywają przydatne w praktyce, są następujące:
mysql_get_client_info char *mysql_get_client_info(void);
Zwraca informacje o wersji biblioteki używanej przez klienta.
mysql_get_host_info char *mysql_get_host_info(MYSQL *connection);
Zwraca informacje o połączeniu z serwerem.
mysql_get_server_info char *mysql_get_server_info(MYSQL *connection);
Zwraca informacje o serwerze, z którym jesteśmy w danym
momencie połączeni.
mysql_info char *mysql_info(MYSQL *connection);
Zwraca informacje o ostatnio wykonanym zapytaniu, lecz dotyczy
to tylko kilku rodzajów zapytań, głównie INSERT i UPDATE. W
innych przypadkach zwraca wartość NULL.
mysql_select_db int mysql_select_db(MYSQL *connection, const char
*dbname);
Zmienia domyślną bazę danych na tę, która jest podana jako
parametr, pod warunkiem, że użytkownik ma właściwe
uprawnienia. W przypadku powodzenia zwracane jest zero.
mysql_shutdown int mysql_shutdown(MYSQL *connection);
Zamyka serwer bazy danych, do którego jesteśmy podłączeni, jeżeli
mamy odpowiednie uprawnienia. W przypadku powodzenia
zwracane jest zero.
Materiały zródłowe
Głównym zródłem informacji o MySQL jest macierzysta strona WWW pod adresem:
http://www.mysql.com.
Książki, do których można zajrzeć to: MySQL, Paul DuBois, wyd. New Riders (ISBN 0-7357-
0921-1) oraz MySQL & mSQL, Randy Jay Yarger, George Reese, Tim King, wyd. O'Reilly &
Associates (ISBN 1-56592-434-7).
Podsumowanie
W tym rozdziale zapoznaliśmy się pobieżnie z MySQL. Nie jest to wprawdzie baza tak bogata we
właściwości, jak PostgreSQL, ale bez wątpienia jest to produkt bardzo wydajny i wyposażony w
interfejs do programów w języku C.
Zobaczyliśmy sposób instalacji i konfiguracji podstawowej bazy danych MySQL i niektóre ważne
polecenia pomocnicze, a następnie zapoznaliśmy się z API dla języka C, jednego z wielu języków,
z których można mieć dostęp do danych w bazie MySQL.
Główną zaletą MySQL w porównaniu z PostgreSQL jest wydajność. Jako baza danych tylko do
odczytu jest ona stosowana w wielu ośrodkach WWW. Jej głównymi słabościami są słabsze
wspomaganie standardu SQL i brak możliwości obsługi transakcji.


Wyszukiwarka

Podobne podstrony:
R 05 07
mini city PL 05 07
Przyklad 8 sup 2014 05 07
TI 01 05 07 T pl(2)
05 07
Log2012 05 07

więcej podobnych podstron