Documente Academic
Documente Profesional
Documente Cultură
Mysql 250 p-6
Mysql 250 p-6
Mysql 250 p-6
Versiunea modificat a funciei do_connect () este similar cu versiunea anterioar^ toate punctele de vedere, cu
dou excepii:
Nu transfer un parametru db_name formei mai vechi a funciei mysql_real_c(j nect (), deoarece versiunea
respectiv nu dispune de un atare parametru.
Dac numele bazei de date nu este NULL, funcia do_connect () ape funcia mysql_select_db() pentru a
transforma baza de date denumit baz de date curent. (Acest procedeu simuleaz efectul paramet db_name, care
lipsete.) Dac baza de date nu poate fi selectat, do_connec afieaz un mesaj de eroare, nchide conexiunea si
returneaz NULL pent indica eecul operaiei.
Exemplul 2. Acest exemplu se bazeaz pe modificrile aduse funciei do_connect( j primul exemplu. Aceste
modificri au ca rezultat trei seturi de apeluri la funciilfl eroare mysql_errno() i mysql_error() i este chiar
obositor s scrii funciile res|| tive de fiecare dat cnd programul trebuie s protesteze" la apariia unei prob| De
asemenea, programul de afiare a erorii este agresiv din punct de vedere vizt dificil de citit. Este mai uor s citii
ceva de genul acesta: print_error(conn, "mysql_real_connect() failed"); Deci, haidei s ncapsulm scrierea
erorilor ntr-o funcie print_error (). Putem : funcia astfel nct s efectueze o anumit operaie chiar si n situaia
cnd conn este l Astfel, putem folosi print_error() dac apelul la funcia mysql_init() eueaz ! avem o
combinaie de apeluri (unele pentru fprintf () i altele pentru print_erroC|
Parc aud pe cineva din spate care obiecteaz: Pi nu suntei obligat s apelai; funcii de eroare de fiecare dat
cnd trebuie s raportai o eroare, deci inteniei: facei programul dificil de citit, pentru ca exemplul
dumneavoastr cu ncapsula arate mai bine. i de fapt nici mcar nu vei scrie tot programul de afiare a erorii
scriei o singur dat i apoi folosii copierea i lipirea dac mai avei nevoie de Acestea sunt observaii corecte,
la care voi rspunde astfel:
Chiar dac folosii copierea i lipirea, aceste operaii sunt mai simplu i tuat cu seciuni mai scurte de program.
Indiferent dac preferai sau nu s invocai ambele funcii de eroare la i raportare a unei erori, scrierea integral
a programului de raportare a i n varianta lung" duce la tentaia de a folosi scurtturi i de a fi inconsa la
raportarea erorilor. Plasarea codului de raportare a erorilor ntr-o container care este simplu de invocat
atenueaz aceast tentat mbuntete consecvena programului.
Dac v decidei vreodat s modificai formatul mesajelor dumneavc eroare, este mult mai uor dac trebuie
s efectuai modificarea ntr-un! loc, dect n tot programul. Sau, dac v decidei s scriei mesajele de < ntr-un
fiier jurnal n loc de (sau n afar de) a le scrie n stderr, este i piu dac trebuie s modificai numai funcia
print_error(). Aceast flfl este mai puin expus la erori si, din nou, reduce tentaia de a face jumtate de treab i
de a fi inconsecvent. *1
Capitolul 6 Interfaa API MySQL pentru C 251
Dac folosii un utilitar de depanare atunci cnd v testai programele, inseria unui punct de ntrerupere n
funcia de raportare a erorilor este o modalitate convenabil de a determina programul s se ntrerup atunci cnd
depanatorul detecteaz o condiie de eroare.
Iat funcia noastr print_error( ) de afiare a erorilor: void print_error (MYSQL *conn, char "message)
{
fprintf (stderr, "%s\n", message); if (conn != NULL)
{
fprintf (stderr, Error %u (%s)\n", mysql_errno(conn) , mysql_error(conn)) ;
Funcia print_error ( ) se afl n fiierul common . c, deci vom aduga un prototip al acesteia n fiierul common .
h:
void
print_error(MYSQL *conn, char "message); Acum, funcia do_connect ( ) poate fi modificat pentru a folosi
funcia print_er ror ( ) :
MYSQL *
do_connect(char *host_natne, char *user_name, char "password, char *db_name, unsigned int portjium, char
*socket_name, unsigned int flags)
{
MYSQL *conn /* pointer spre variabila de tratare a conexiunii */
conn = mysql_init (NULL); /* aloca, iniializeaz variabila
de tratare a conexiunii */ if (conn == NULL)
{
print_error(NULLj "mysql_init()f ailed (probably out of memory)"); return (NULL);
} #if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 32200
/* versiunea 3.22 si versiunile ulterioare */ if (mysql_real_connect (conn, host_name, user_name, password,
db_name, port_num, socket_name, flags) == NULL)
{
print_error(conn, "mysql_real_connect() failed"); return (NULL);
#else
/* pentru MySQL anterior versiunii 3.22 */
Continuare
252 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
if (mysql_real_connect (conn, nost_name, user_name, password, port_num, socket_name, flags) == NULL)
print_error(conn, "mysql_real_connect() failed"); return(NULL);
if (db_name != NULL) /* simularea efectului parametrului db_name */J if (mysql_select_db (conn, db_name) !=
0)
print_error(conn, "mysql_select_db() failed");
mysql_close(conn);
return(NULL);
#endif
return (conn);
/* conexiunea a fost stabilita */
Fiierul nostru surs principal, clients.c, este asemntor cu client2.c, dar auj| eliminate toate liniile de program
de conexiune i ntrerupere a conexiunii, f^ nlocuite cu apeluri la funciile container. Deci, fiierul surs se
prezint astfel: /* clienta.c */
tfinclude <stdio.h> #include <mysql.h> tfinclude "common.h"
tfdefine def_host_name NULL tfdefine def_user_name NULL
#define def_password NULL ^define def_db_name NULL
MYSQL *conn;
int
main (int argc, char *argv[])
/* gazda la care se va stabili conexi
nea(valoare prestabilita = localhos /* nume utilizator (valoare prestabil
= numele dumneavoastr de deschide
a sesiunii de lucru) */ /* parola (valoare prestabilita =
nici una) */ /* baza de date de utilizat (valoare J
prestabilita = nici una) */
/* pointer spre variabila de tratare conexiunii */
Capitolul 6 Interfaa API MySQL pentru C 253
conn = do_connect(def_host_name, def_user_name, def_password,
def_db_name, def_port_num, def_socket_name, 0); if (conn == NULL) exit(1);
/* aici are loc activitatea aplicaiei */
do_disconnect(conn); exit(O);
Client 4 - Obinerea parametrilor de conexiune la rulare
Acum, cnd dispunem de un program de conexiune uor de modificat si blindat" n cazul apariiei erorilor,
suntem pregtii s aflm cum putem face lucruri mai inteligente dect s folosim parametri de conexiune
NULL, ca de exemplu s permitem utilizatorului s specifice aceste valori la rulare.
Clientul anterior, clients, continu s prezinte un dezavantaj semnificativ, n sensul c parametrii de conexiune
sunt codai n program. Pentru a modifica oricare dintre aceste valori, trebuie s editai fiierul surs i s-1
recompilai, ceea ce nu este foarte convenabil, mai ales dac dorii s punei programul la dispoziia altor
persoane.
O modalitate frecvent folosit de specificare a parametrilor de conexiune la rulare este de a folosi opiunile din
linia de comand. Programele din distribuia sistemului MySQL accept parametrii de conexiune ntr-una din
cele dou forme specificate n tabelul 6.1.
Tabelul 6.1 Opiuni standard din linia de comand pentru MySQL
Parametru
Numele gazdei Numele de utilizator Parola
Numrul portului Numele soclului
Form scurt
-h nume_gazda
-u nume_utilizator
-p sau -pparola_dv
-P numar_port -S nume soclu
Form lung
- - host=mj/ne_gazc/a
- -user=nume_i/tilizator
--password sau
--password=parola_dv
- -port=/iu/nar_port --socket=nume soclu
L
Pentru consecvena cu programele client MySQL standard, clientul nostru va accepta aceleai formate, ceea ce
este simplu de realizat, deoarece biblioteca client include o 'Vineie pentru analiza opiunilor.
h plus, clientul nostru va putea s extrag informaiile din fiierele cu opiuni. Aceasta y permite s plasai
parametrii de conexiune n fiierul - /. my. cnf (adic fiierul . my. cnf din catalogul dumneavoastr de baz),
astfel nct s nu fie necesar specificarea lor n de comand. Biblioteca client faciliteaz cutarea fiierelor cu
opiuni MySQL i extragerea din acestea a tuturor valorilor relevante. Prin adugarea a numai cteva linii
254 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
n programul dumneavoastr, putei determina programul s recunoasc fiiere!^ J opiuni si nu trebuie s
reinventai roata prin scrierea propriilor dumneavoastr p| grame pentru aceasta. Sintaxa fiierelor cu opiuni este
descris n Anexa E, Refer* de programe MySQL".
Accesul la coninutul fiierelor cu opiuni
Pentru a citi fiierele cu opiuni n cutarea valorilor parametrilor de conexiune, folc funcia load-def aults().
Aceast funcie caut fiierele cu opiuni, le analizeaz cont tul pentru a descoperi orice grupuri de opiuni care
v intereseaz si rescrie vector argumente al programului dumneavoastr (tabloul argv[ ]) pentru a insera infor
din aceste grupuri sub form de opiuni ale liniei de comand la nceputul table argv [ ]. Astfel, opiunile apar ca
i cum ar fi fost specificate n linia de comand. Ca; cnd analizai opiunile comenzii, obinei parametrii de
conexiune ca parte a cif normal de analiz a opiunilor. Opiunile sunt adugate la nceputul tabloului argv( nu la
sfrit, astfel nct, dac parametrii de conexiune sunt ntr-adevr specificai rn de comand, acetia s apar mai
trziu (si implicit s redefineasc) orice opiuni adt de funcia load_def aults ().
J"
Iat un mic program, show_argv, care prezint modul de utilizare a funciei load_def aur i care ilustreaz
modificarea vectorului cu argumente prin acest procedeu: /* show_argv.c */
^include <stdio.h> ^include <mysql.h>
char *groupsl] = { "client", NULL };
int
main(int argc, char *argv[])
{
int i;
my_init();
printf("Vector cu argumente original:\n"); for(i = 0; i < argc; i++)
printf("arg %d: %s\n", i, argvi]);
load_defaults("my", groups, &argc, &argv);
printf("Vector cu argumente modificat:\n"); for(i = 0; i < argc; i++)
prirvtf ("arg %d: %s\n", i, argv[i]);
exit(O);
Capitolul 6 Interfaa API MySQL pentru C 255
Programul de prelucrare a fiierului cu opiuni implic urmtoarele:
groups [] este un tablou ir de caractere care indic grupurile din fiierele cu opiuni care v intereseaz. Pentru
programele client, specificai ntotdeauna cel puin meniunea "client" (pentru grupul [client]). Ultimul element
al tabloului trebuie s fie NULL.
my_init () este o rutin de iniializare care execut unele operaii de pornire impuse de funcia load_defaults().
Funcia load_def aults () preia patru argumente: prefixul fiierelor dumneavoastr cu opiuni (acesta trebuie s
fie ntotdeauna "my"), tabloul care menioneaz grupurile de opiuni care v intereseaz, respectiv adresa
numrului de argumente i a vectorului de argumente ale programului dumneavoastr. Nu transferai valorile
numrului de argumente i ale vectorului; transmitei n schimb adresele lor, deoarece funcia load_def aults ()
trebuie s le modifice valorile. Reinei, mai ales, c dei argv este un pointer, trebuie s transferai &argv, adresa
pointerului respectiv.
Funcia show_argv i afieaz argumentele de dou ori: prima dat aa cum le-ai specificat n linia de comand,
apoi n urma modificrilor efectuate de load_defaults(). Pentru a vedea efectele funciei load_def aults (),
asigurai-v c avei un fiier .my. cnf n catalogul dumneavoastr de baz, cu unii parametri specificai pentru
grupul [client]. S presupunem c fiierul .my .cnf se prezint astfel:
[client]
user=paul
password=secret
host=o_gazda n aceast situaie, prin executarea programului show_argv se obine urmtorul rezultat:
% show_argv a b
Vector cu argumente original:
arg 0: show_argv
arg 1; a
arg 2: b
Vector cu argumente modificat:
arg 0: show_argv
arg 1; --user=paul
arg 2: --password=secret
arg 3; --host=o_gazda
arg 4: a
arg 5: b
Este posibil ca ntre datele de ieire ale programului show_argv s vedei unele opiuni care nu se gseau nici n
linia de comand, nici n fiierul dumneavoastr -/ .my .cnf. n acest caz, opiunile respective au fost probabil
specificate ntr-un fiier cu opiuni la nivel de sistem. De fapt, funcia load_def aults () caut fiierele /etc /my
.cnf si my .cnf din catalogul de date MySQL nainte de a citi fiierul .my.cnf din catalogul dumnea-I voastr de
baz. (n Windows, funcia load_defaults() caut fiierele C:\my.cnf, l c: \mysql\data\my. cnf, respectiv fiierul
my. ini din catalogul \Windows\System).
256 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Programele client care folosesc funcia load_def aults () specific aproape ntc "client" n lista cu grupuri d
opiuni (pentru a putea obine tpate valorile client^ rale din fiierele cu opiuni), dar putei cere i valori care sunt
specifice propriulvuj neavoastr program. Pur i simplu nlocuii instruciunea
char *groups[] = { "client", NULL }; cu urmtoarea linie de
program: , ;||
char *groups.[J = { "show^arov", "client", NULL }; Apoi, putei aduga un grup [show_argv] la fiierul
dumneavoastr -/ .my.cnf: sj.
[client]
user=paul ?
password=secret
host=o_gazda
[show_argvj
hostalta_gazda ;: -
n urma acestor modificri, o nou invocare a programului show_argv va ave rezultat, dup cum urmeaz:
% show_argv a b '
Vector cu argumente original:
arg 0: show_argv
arg 1: a
arg 2: b
Vector cu argumente modificat:
arg 0: show_argv
arg 1: --user=paul
arg 2: --password=secret
arg 3: --host=o_gazda
arg 4: --host=alta_gazda
arg 5: a
arg 6: b
Ordinea n care apar valorile opiunilor n tabloul cu argumente este deterr ordinea n care acestea sunt
menionate n fiierul dumneavoastr cu opk ordinea n care grupurile cu opiuni sunt menionate n tabloul
groups [J. , nseamn c probabil vei dori s specificai grupuri specifice programelor [client] din fiierul
dumneavoastr cu opiuni. Astfel, dac specificai 6 o| ambele grupuri, valoarea specific programului va avea
prioritate. Putei vedea; n exemplul prezentat anterior: opiunea host a fost specificata att rl grupul'^ ct si n
grupul [ show_argv ] dar, deoarece grupul [ show_argv] apare ultimul n \ opiuni, valoarea sa host apare mai
trziu n vectorul cu argumente i are pric
Funcia load_def aults () nu selecteaz valori din parametrii dumneavoastr^ Dac doni s folosii valorile unor
variabile de mediu precum MYSQL_TCP MYSQL_UNIX_PORT, trebuie s v ocupai personal de acest lucru
prin intermediul l getenv(). Nu voi aduga aceast funcionalitate la clienii notri, dar iat un i verificare a
valorilor a dou dintre variabilele de mediu standard legate de M;
Capitolul 6 Interfaa API MySQL pentru C 257
extern char *getenv(); char *p; u: int port_nura; char *oqKet_name;
if ((p, = OetenvCMYSQL^TCP^PQRT") != NUU),
portjium, ?. atoi (p); r if ((P QetewMYSQLJJNIXJ'OR'!) 1= NU14-)
socketjiame = p; , , ;. -.;
n cazul clienilor MySQL standard, valorile variabilelor de mediu au o prioritate mai redus dect valorile
specificate n fiierele cu opiuni sau n linia de comand. Dac verificai variabilele membru i doriri s
respectai convenia respectiv, verificai variabilele de mediu nainte, nu dup apelarea funciei load_def aults ()
sau prelucrarea opiunilor din linia de comand.
- 't
Analiza argumentelor din linia de comand
n acest moment, putem prelua toi parametrii de conexiune n vectorul cu argumente, dar avem nevoie de o
modalitate de analiz a vectorului. Funcia getoptO*are exact aceast destinaie.
Funcia getopt_long() este ncorporat n biblioteca client MySQL, deci putei avea acces la aceasta ori de cte
ori stabilii legturi cu funcii din biblioteca respectiv, n fiierul dumneavoastr surs, trebuie si includei
fiierul antet getopt. h. Putei copia acest fiier antet din catalogul include al distribuiei surs MySQL n
catalogul n care v dezvoltai programul client. !< ' "r -' ;;
i . . 't. C -.-": ' -
Funcia load_def aults () i sewritoteo
Poate v punei ntrebri cu privire la implicaiile legate de .spionarea" proceselor pe care le poate avea
solicitarea ca funcia load_def aults () s insereze textul parolelor n lista dumneavoastr cu argumente, deoarece
programe precum ps pot afia listele cu argumente pentru procese arbitrare. Nu este nici o problem, deoarece ps
afieaz coninutul original al tabloului argv[ ]. Toate argumentele de tip parol create de funcia load_def aults ()
indic spre o regjune pe care funcia respectiv o aloc pentru sine. Acea regiune nu face parte din vectorul
original, deci programul ps nu o vede niciodat.
Pe de alt parte, o parol care este specificat n linia de comand apare n ps dac dumneavoastr nu v ngrijii
s o tergei. Seciunea .Analiza argumentelor din linia de comand" v arat cum s procedai.
Programul urmtor, show_param, folosete funcia load_defaults() pentru citirea fiierelor cu opiuni, apoi
apeleaz-funcia getopt_long<) pentru analiza vectorului cu argumente. show_param arat ce se ntmpl n
fiecare faza a prelucrrii argumentelor, Prin efectuarea urmtoarelor aciuni: ! Configureaz valorile prestabilite
pentru numele gazdei, numele de utilizator i parola.
2- Afieaz valorile originale ale parametrilor de conexiune i valorile din vectorul cu
argumente. ' ; u''
258 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
3. Apeleaz funcia load_defaults() pentru a rescrie vectorul cu argumente astfdij acesta s reflecte coninutul
fiierului cu opiuni, dup care afieaz vectorul re
4. Apeleaz funcia getopt_long () pentru prelucrarea vectorului cu argumente, ap seaz valorile rezultante ale
parametrilor i ceea ce a mai rmas n vectorul cu :
show_param v permite s exersai diferite modaliti de specificare a parametrik conexiune (situai n fiierele
cu opiuni sau n linia de comand) i s vedei rezult prin afiarea valorilor care vor fi folosite pentru stabilirea
unei conexiuni. show_ este util pentru a v face o idee privind ceea ce se va ntmpla n urmtorul nostru^ gram
client, atunci cnd corelm acest program de prelucrare a parametrilor cu noastr de conectare do_connect ().
Iat cum se prezint fiierul show_param.c: /* show_param.c */
^include <stdio.h>
tfinclude <stdlib.h> /* necesar pentru atoi() */
^include "getopt.h"
char *groups[] = { "client", NULL };
struct option long_options[] =
{"host",
{"user",
{"password",
{"port",
{"socket",
{ 0, 0, 0, 0 }
required_argument, required_argument, optional_argument, required_argument,
NULL, NULL, NULL, NULL,
'u'},
'P'}, 'P'},
required_argument, NULL, 'S'},
int
main (int argc, char *argv[])
char *host_name = NULL; char *user_name = NULL; char *password = NULL; unsigned int portjium = 0; char
*socket_name = NULL; int i; int c, option_index;
my_init();
printf ("Parametrii originali ai conexiunii:\n")
Capitolul 6 Interfaa API MySQL pentru C 259
printf ("host name: %s\n" , host_name ? hostjiame : "(nuli)"};
printf ("user name: %s\n", user_name ? user_name : "(nuli)");
printf ("password: %s\n", password ? password : "(null)");
printf ("port number: %u\n", port_num);
printf ("socket name: %s\n", socket_name ? socket_name : "(null)");
printf ("Vector cu argumente original: \n" ); for (i = 0; i < argc; i++)
printf ("arg %d: %s\n", i, argv[i]);
load_defaults("my", groups, &argc, &argv);
printf ("Vector cu argumente modificat dup load_defaults() :\n"); for (i = 0; i < argc; i++)
printf (" arg %d: %s\n" , i, argv[i]);
while ((c = getopt_long(argc, argv, "h:p: :u:P:S" , long_options, &option_index)) != EOF)
{
switch (c)
{
case ' h ' :
host_name = optarg;
break; case 'u':
user_name = optarg;
break; case ' p ' :
password = optarg;
break; case 'P' :
portjnum = (unsigned int) atoi (optarg);
break; case 'S' :
socket_name = optarg; break;
argc -= optind; /* avanseaz dincolo de argumentele */
argv += optind; /* care au fost prelucrate de getopt_long() */
printf ("Parametrii conexiunii dup getopt_long() :\n") printf ("host name: %s\n", host_name ? hostjiame :
"(nuli)");
Continuare
260 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
printf ("user name: %s\n", user_name ? user_name : "(nuli)");
printf ("password: %s\n", password ? password : "(null)");
printf ("port number: %u\n", port_num);
printf ("socket name: %s\n", socket_name ? socket_name : "(null)");
printf ("Vector cu argumente dup getopt_long():\n"); for (i = 0; i < argc; i++)
printf("arg %d: %s\n", i, argv[i]); exit(O);
}
Pentru a prelucra vectorul cu argumente, show_argv folosete funcia getopt_long (), care o apelai, n mod
caracteristic, ntr-un ciclu: 3
while ((c = getopt_long(argc, argv, "h: p: :u:P:S", long_options, &option_index)) != EOF)
{
/* opiune de proces */
}
Primele dou argumente ale funciei getopt_long() le constituie numrul de argur al programului dumneavoastr
i vectorul cu argumente. Al treilea argument l re zint literele de opiuni care dorii s fie recunoscute. Acestea
sunt formele cu mj scurt ale opiunilor programului dumneavoastr. Literele de opiune pot fi urmat dou puncte,
de dou puncte dublate (::) sau de nici un asemenea caracter, pent arta c opiunea trebuie s fie urmat, poate fi
urmat, respectiv nu este urmat valoare a opiunii. Cel de-al patrulea argument, long_options, este un pointer
spr tablou cu structuri de opiune, fiecare din aceste structuri specificnd informaii ] o opiune pe care dorii ca
programul dumneavoastr s o neleag. Rolul su este st| Iar cu acela al irului de opiuni din al treilea
argument. Cele patru elemente ale fiec structuri long_opt ions [] sunt urmtoarele:
Numele lung al opiunii.
O valoare a opiunii. Valoarea poate fi required_argument (argument obligate optional_argument (argument
facultativ) sau no_argument (fr argument), indic dac opiunea trebuie urmat, poate fi urmat, respectiv nu
este urmat valoare a opiunii. (Aceste valori au acelai rol ca si caracterul dou puncte, car dou puncte dublate
si respectiv absena oricrui caracter din al treilea argument, < conine irul de opiuni.)
Un argument indicator (flag). Putei folosi acest argument pentru a stoca un j
la o variabil. Dac se gsete opiunea, getopt_long () stocheaz n variabil valet specificat de al patrulea
argument. Dac indicatorul este NULL, getopt_long () coK reaz variabila optarg astfel nct s indice spre orice
valoare care urmeaz dup of i returneaz numele scurt al opiunii, n cazul nostru, tabloul long_options[ ] sg
fic NULL pentru toate opiunile. Astfel, getopt_long () returneaz fiecare argumefl cum l ntlnete astfel nct
s poat fi prelucrat n instruciunea switch.
Capitolul 6 Interfaa API MySQL pentru C 261
Numele scurt (dintr-un singur caracter) al opiunii. Numele scurte precizate n tabloul long_options[ ] trebuie s
corespund literelor folosite n irul cu opiuni pe care l transferai drept al treilea argument al funciei
getopt_long(), n caz contrar programul dumneavoastr fiind incapabil de a prelucra n mod adecvat argumentele
din linia de comand.
Tabloul long_options[] trebuie terminat cu o structur ale crei elemente sunt toate egale cu 0.
Cel de-al cincilea argument al funciei getopt_long() este un pointer la o variabil de tip int. getopt_long ()
stocheaz n aceast variabil indexul structurii long_options [ ] care corespunde ultimei opiuni ntlnite.
(show_param nu face nimic cu aceast valoare.)
Reinei c opiunea pentru parol (specificat sub forma - -password sau -p) poate lua o valoare opional. Cu
alte cuvinte, o putei specifica sub forma password sau --pass-word=parola_dumneavoastr dac folosii forma
de opiune lung, respectiv -p sau
- -pparola_dumneavoastra dac folosii forma de opiune scurt. Natura opional a valorii parolei este indicat!
de caracterul:: plasat dup litera p din irul cu opiuni, precum i de specificaia optional_argument din tabloul
long_options[]. De regul, clienii MySQL v permit s omitei valoarea parolei n linia de comand, dup care
v solicit aceast valoare. Acest procedeu v permite s evitai furnizarea parolei n linia de comand, ceea ce
mpiedic pe alii s v citeasc parola prin spionarea" procesului. Cnd vom scrie urmtorul client, client4,
vom aduga la acesta logica de verificare a parolei.
Iat un exemplu de invocare a programului show_param si a datelor de ieire rezultate (presupunnd c -/.my.cnf
are acelai coninut ca n exemplul cu programul show_argv):
% show_paran -h inca_o_gazda x
Parametri de conexiune originali:
host name: (null)
user name: (null)
password: (null)
port number: 0
socket name: (null)
Vector cu argumente original:
arg 0: show_parara
arg 1: -h
arg 2: inca_o_gazda
arg 3: x
Vector cu argumente modificat dup load_defaults<):
arg 0: show_param
arg 1: --user=paul
arg 2: --password=secret
arg 3: --host=o_gazda
arg 4: -h
arg 5: inca_o_gazda
Continuare
262 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
arg 6: x
Parametri de conexiune dup getopt_long():
host name: inca_o_gazda
user name: paul
password: secret
port number : O
socket name: (null)
Vector cu argumente dup getopt_long():
arg 0: x
Datele de ieire arat c numele gazdei este selectat din linia de comand (redc valoarea din fiierul cu opiuni),
precum si c numele de utilizator si parola provin ld fiierul cu opiuni. getopt_long() analizeaz corect opiunile,
indiferent dac ac sunt specificate n forma de opiune scurt (-h nume_gazda) sau n forma de op lung (--
user=paul, --password=secret).
Acum, s eliminm liniile de program care nu au dect rolul de a ilustra modii funcionare a rutinelor de tratare a
opiunilor i s folosim restul programului cai pentru un client care se conecteaz la un server n conformitate cu
opiunile cari i furnizate ntr-un fiier cu opiuni sau n linia de comand. Fiierul surs re client4.c, se prezint
astfel: /* client4.c */
tfinclude <stdio.h>
#include <stdlib.h> /* pentru atoi() */
#include <mysql.h>
#include "common.h"
^include "getopt.h"
#define def_host_name NULL tfdefine def_user_name NULL
tfdefine def_password NULL #define def db name NULL
/* gazda la care se va stabili
conexiunea(valoare prestabilita = .<
localhost) */ /* nume utilizator (valoare prestabil
numele dumneavoastr de deschidere^
sesiunii de lucru) */ /* parola (valoare prestabilita = nicij
una) */ /* baza de date de utilizat (valoare "
prestabilita = nici una) */
char *groups[] = { "client", NULL }; struct option long_options[] =
{"host", required_argument, NULL, 'h'},
{"user",
{"password",
{"port",
{"socket",
{ O, O, O, O }
Capitolul 6 Interfaa API MySQL pentru C 263
required_argument, NULL, 'u'},
optional_argument , NULL, 'p'},
required_argument , NULL, 'P'},
required_argument, NULL, 'S'},
MYSQL *conn; /* pointer spre variabila de tratare a
conexiunii */
int
main (int argc, char *argv{])
char *host_name = def_host_name;
char *user_name = def_user_name;
char *password = def_password;
unsigned int portjium = def_port_num;
char *socket_name = def_socket_name;
char *db_name = def_db_natne;
char passbuf[100];
int ask_password = 0;
int i;
int c, option_index=0;
my_init();
load_defaults("my", groups, &argc, &argv);
while ((c = getopt_long(argc, argv, "h:p::u:P:S", long_options, &option_index)) != EOF)
switch (c)
case 'h1:
host_name = optarg;
break; case 'u':
user_name = optarg;
break; case 'p':
if (!optarg) /* nu este data nici o valoare */ ask_password = 1;
else /* copiaz parola, terge originalul */
Continuare
264 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
i
(void) strncpy (passbuf, optarg, sizeof (passbuf )-1); passbuf [sizeof (passbuf ) -1 ] = '\0'; password = passbuf;
while (*optarg)
*optarg++ = ' ' ; }
break; case 'P1:
port_num = (unsigned int) atoi (optarg); break; case 'S1:
socket_name = optarg; break;
argc -= optind; /* avanseaz dincolo de argumentele */ argv += optind; /* prelucrate de getopt_long ( ) */ if
(argc > 0)
{
db_name = argv[0]; --argc; ++argv;;
if (ask_password)
password = get_tty_password (NULL);
conn = do_connect (host_name, user_name, password,
db_name, port_num, socket_name, 0 if (conn == NULL) exit(1);
/* aici se insereaz codul aplicaiei propriu -zise */
do_disconnect(conn) ; exit(O); }
n comparaie cu programele clieni, client2 i clients pe care le-am creat ane client4 efectueaz cteva operaii
pe care nu le-am ntlnit pn acum:
Permite specificarea numelui bazei de date n linia de comand, dup opiunii* i sunt analizate de getopt_long (
) . Aceast comportare este similar cu aceea a clie standard din distribuia MySQL.
O
1.
2.
L
schi general a algoritmului de afiare este urmtoarea:
Se determin limea de afiare pentru fiecare coloan.
Se afieaz un rnd de etichete de coloan ncasetate" (delimitate prin bare verticale
i precedate, respectiv urmate de rnduri formate din liniue).
Se afieaz valorile din fiecare rnd al setului de rezultate, cu fiecare coloan ncase-tat (delimitat prin bare
verticale) i aliniat pe vertical, n plus, numerele sunt afiate aliniate la dreapta i se afieaz cuvntul "NULL"
n locul valorilor NULL.
280 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
4. La sfrit, se afieaz un numr al rndurilor regsite. Acest exerciiu reprezint o bun demonstraie a
utilizrii metadatelor setului de n ae. Pentru a afia rezultatele aa cum s-a artat mai sus, trebuie s tim mai
lucruri despre setul de rezultate, nu numai valorile datelor incluse n rnduri.
Poate v gndii: Hmm, descrierea aia arat suspect de asemntor cu modul n mysql i afieaz rezultatele".
Da, aa este, iar dumneavoastr suntei invitat s parai codul surs al programului mysql cu programul
final al fum process_result_set() revizuite. Cele dou nu sunt identice, dar comparaia dou abordri ale
aceleiai probleme este instructiv.
Mai nti, trebuie s determinm limea de afiare a fiecrei coloane. Listingul v arat cum s procedai.
Calculele se bazeaz n ntregime pe metadatele setuli rezultate i nu se face nici o referire la valorile din rnduri:
MYSQL_FIELD *field;
unsigned int i, col, len;
/* determina limile de afiare ale coloanelor
mysql_field_seek (res_set, 0);
for (i = 0; i < mysql_num_fields (res_set);
;j
rj
|
vj
Ai
'3i
j*
4
'
field = mysql_fetch_field (res_set); col_len = strlen (field->narae); if (col_len < field->max_length)
col_len = field->max_length; if (col_len < 4 && IIS NOT NULL (field->flags))
col_len = 4; /* 4 este lungimea cuvntului "NULL" */ field->max_length = col_len; /* reinitializarea
informaiilor
despre coloana */
Limile coloanelor sunt calculate prin parcurgerea ciclic a structurilor MYSQL_FI! pentru coloanele din setul
de rezultate. Ne poziionm pe prima structur prin apela funciei mysql_fetch_seek(). Apelurile ulterioare la
funcia mysql_fetch_fiel| returneaz pointer! spre structurile coloanelor succesive. Limea unei coloane afiare
o reprezint cea mai mare din trei valori, fiecare depinznd de metadat&e structura cu informaii privind
coloanele:
Lungimea cmpului f ield->name, titlul coloanei.
f ield->max_length, dimensiunea celei mai lungi valori a datelor din coloan.
Lungimea irului" NULL", n cazul n care coloana poate conine valori NULL, arat dac acea coloan poate
conine NULL sau nu.
Reinei c, dup ce limea de afiare pentru coloan a devenit cunoscut, at aceast valoare variabilei
max_length, care este un membru al unei structuri pe obinem din biblioteca client. Acest lucru este permis sau
este necesar ca dateiey structura MYSQL_FIELD s fie considerate ca fiind numai pentru citire? n mod nor
opta pentru a doua variant, dar unele dintre programele client din distribuia MyS
&',*.' " ' '
Capitolul 6 Interfaa AP) MySQL pentru C 281
modific valoarea max_length ntr-un mod asemntor, deci voi presupune c procedeul este permis. (Dac
preferai o abordare alternativ, care nu modific max_length, alocai un tablou de valori unsigned int i stocai
limile calculate n tabloul respectiv.)
Calculul limilor de afiare ascunde o capcan. V mai reamintii c max_length nu are nici o semnificaie
atunci cnd creai un set de rezultate folosind mysql_use_result(). Deoarece avem nevoie de max_length pentru a
determina limea de afiare a valorilor din coloan, funcionarea corect a algoritmului impune ca setul de
rezultate s fie generat folosind mysql_store_result( )2.
Odat cunoscute limile coloanelor, suntem gata de afiare. Titlurile sunt uor de manipulat; pentru o coloan
dat, folosim pur si simplu structura cu informaii despre coloan indicat prin field i afim membrul name,
folosind limea calculat anterior:
printf (" %-*s |", field->max_length, field->name);
Pentru date, vom parcurge ciclic rndurile din setul de rezultate, afind valorile coloanelor pentru rndul curent
n timpul fiecrei iteraii. Afiarea valorilor din coloanele unui rnd este oarecum ciudat, deoarece o valoare
poate fi NULL sau poate reprezenta un numr (caz n care l afim aliniat la dreapta). Valorile coloanelor sunt
afiate dup cum urmeaz, unde row [ii conine valorile datelor, iar field indic spre informaiile despre coloane:
if (row[i] = = NULL)
printf (" %-*s |", field->max_length, "NULL");
else if (IS_NUM (field->type) )
printf (" %*s |", field->max_length, row[i]);
else
printf (" %-*s l", field->max_length, row[il);
Valoarea macroinstruciunii IS_NUM() este adevrat dac tipul coloanei indicat de field ->type este un tip
numeric, precum INT, FLOAT sau DECIMAL.
Liniile de program finale pentru afiarea setului de rezultate se prezint astfel. Reinei c, deoarece afim de
mai multe ori linii compuse din mici liniue, codul pentru aceast operaie este ncapsulat n propria sa funcie,
print_dashes ( ) :
void
print_dashes (MYSQL_RES *res_set)
MYSQL_FIELD unsigned int
*field; i, j;
mysql_field_seek (res_set, 0);
f pute ('*', stdout) ;
for (i = 0; i < mysql_num_fields (res_set); i
{
field = mysql_fetch_field (res_set);
Continuare
1 Membrul length al structurii MYSQL_FIELD v indic lungimea maxim pe care o pot avea valorile din
coloane. Aceasta poate fi o soluie util dac folosii mysql_use_result() n locul funciei mysql_store_result().-
N.A.
282 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
for (j = 0; j < field->max_length + 2; j +-*)
fputcC-', stdout); f pute (' + ', stdout);
}
f pute ( '\n' , stdout);
void
process_result_set (MYSQL *conn, MYSQL_RES *res_set)
MYSQL_FIELD
MYSQL_ROW
unsigned
*field;
row
int i, col, len;
/* determina limile de afiare ale coloanelor */
mysql_field_seek (res_set, 0);
for (i = 0; i < mysql_num_fields (res_set); i
field = mysql_fetch_field (res_set); col_len = strlen (f ield->name) ; if (col_len < field->max_length)
col_len = field->max_length; if (col_len < 4 && !IS NOT NULL (field->flags))
col_len =4; /* 4 este lungimea cuvntului "NULL" */ field->max_length = col_len; /* reinitializarea
informaiilor
despre coloana */
print_dashes (res_set)
f pute ('l1, stdout);
mysql_field_seek (res_set, 0);
for (i = 0; i < mysql_num_fields (res_set);
field = mysql_fetch_field (res_set);
printf (" %-*s |", field->max_length, field->name) ; }
f pute ('\n', stdout); print_dashes (res_set);
while ((row = mysql_fetch_row (res_set)) 1= NULL)
{
mysql_field_seek (res_set, 0); f pute (' l' i stdout);
Capitolul 6 Interfaa API MySQL pentru C 283 for (i = 0; i < mysql_num_fields (res_set);
field = mysql_fetch_field (res_set); if (row[i] = = NULL)
printf (" %-*s I", field->max_length, "NULL"); else if (IS_NUM (field->type))
printf (" %*s |", field->max_length, row[i]); else
printf (" %-*s |", field->max_length, row[i]);
}
fputc ('\n', stdout);
}
print_dashes (res_set);
printf ("%lu rnduri returnate\n", (unsigned long)
mysql_num_rows (res_set)); }
Biblioteca client MySQL furnizeaz numeroase modaliti de acces la structurile cu informaii despre coloane.
De exemplu, programul din exemplul anterior obine de mai multe ori accesul la aceste structuri, folosind cicluri
avnd urmtoarea form general: mysql_field_seek (res_set, 0); for (i = 0; i < mysql_num_fields (res_set);
{
field = mysql_fetch_field (res_set);
Cu toate acestea, combinaia dintre funciile mysql_f ield_seek () si mysql_f etch_f ield () este numai una din
modalitile de a obine structurile MYSQL_FIELD. Pentru a afla despre alte modaliti de obinere a structurilor
cu informaii despre coloane, vezi informaiile aferente funciilor mysql_fetch_f ields() i mysql_f etch_f
ield_direct() din Anexa F.
Client 5 - Un program interactiv de interogare
S adunm o buna parte din programele create pn acum si s le folosim pentru a scrie un program client
interactiv simplu. Acesta v permite s introducei interogri, le execut folosind rutina noastr de uz general
pentru prelucrarea interogrilor process_query() i afieaz rezultatele folosind programul de formatare a datelor
de ieire process_result_set (), creat n seciunea precedent.
clients va fi similar din multe puncte de vedere cu mysql, dei nu va avea, desigur, un numr att de mare de
caracteristici. Exist numeroase restricii cu privire la datele de intrare care vor fi acceptate de clients:
* Fiecare linie de intrare trebuie s conin o singur interogare complet.
* Interogrile nu trebuie s se ncheie cu punct i virgul sau cu \g.
* Comenzi precum quit nu sunt recunoscute; n schimb, folosii Control-D pentru a termina execuia
programului.
284 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Se observ c redactarea programului clients este aproape banal (sub 10 linii de pi gram noi). Aproape toate
elementele necesare sunt furnizate.de scheletul prograrm) client (client4.c) si de alte programe pe care le-am
scris deja. Singurul lucru care trebuie adugat este un ciclu care adun datele de intrare si le execut. Pentru a
construi programul clients, ncepei prin a copia scheletul de program < client4.c n programul clients.c. Apoi
adugai la acesta liniile de program afere funciilor process_query(), process_result_set() i print_dashes(). n
final, n p*i gramul clients. c, cutai linia din funcia main () care are urmtorul coninut:
/* aici se insereaz codul aplicaiei propriu-zise */ Apoi nlocuii-o cu urmtorul ciclu while:
while (1)
char buf[1024];
fprintf (stderr, "query> "); if (fgets (buf, sizeof (buf);
break; process_query (conn, buf);
/* prompt de afiare */ stdin) = = NULL) /* citete interogarea */
/* executa interogarea */
Compilai programul clients.c pentru a produce fiierul clients.o, legai clienii cu common.o i cu biblioteca
client pentru a genera fiierul clients i ai terminat! Aw un program client MySQL interactiv, care poate executa
orice interogare si afi rezultatele.
Alte aspecte
Aceast seciune trateaz numeroase subiecte care nu s-au putut ncadra foarte bit evoluia de la clieni la clients:
'
Utilizarea datelor din setul de rezultate pentru a calcula un rezultat, dup utilia metadatelor din setul de
rezultate pentru a contribui la verificarea faptului c sunt adecvate pentru calculul dumneavoastr.
Modul de tratare a datelor dificil de inserat n interogri.
Modul de lucru cu datele de tip imagine.
Modul de obinere a informaiilor referitoare la structura tabelelor dumneavoas
Greeli comune de proiectare n MySQL i modul de evitare a acestora.
Efectuarea de calcule cu seturile de rezultate
Pn acum, ne-am concentrat asupra utilizrii metadatelor aferente seturilor de rezul mai ales pentru afiarea
datelor din rnduri, dar este evident c vor exista situaii " datele dumneavoastr vor trebui folosite i n alte
moduri, nu numai afiate. De exti putei efectua calcule statistice n funcie de valorile datelor, folosind
metadatele v asigura c datele se conformeaz cerinelor pe care dorii ca acestea s le satisfa
imagine_id int(11)
imagine_data blob YES PRI i 0 i NULL
Dac executai aceeai interogare din propriul dumneavoastr client, vei primi aceleai iformaii (fr casete).
290 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Dac dorii informaii numai despre o singur coloan, folosii aceast interogare:
SHOW FIELDS FROM nume_tabel LIKE "nume_coloana" Interogarea va returna aceleai coloane, dar un
singur rnd (sau nici un rnd, n cazul rJ care coloana nu exist).
Greeli de programare a programelor client care trebuie evitate
Aceast seciune discut despre unele erori comune de programare a interfeelor . pentru MySQL scrise n C,
precum i despre modul de evitare a acestora. (Aceste pr bleme apar periodic n lista de coresponden despre
MySQL; nu le-am inventat eu.)
Greeala 1: Utilizarea unor pointer! neiniializai ai variabilelor de tratare a conexiunilor
n exemplele prezentate n acest capitol, am apelat funcia mysql_init() transfernd acesteia un argument NULL.
Astfel, se indic funciei mysql_init () s aloce i s inirializ o structur MYSQL i s returneze un pointer spre
aceasta. O alt abordare o consrit transferul unui pointer spre o structur MYSQL, n acest caz, mysql_init () va
iniializa; structur i va returna un pointer ctre aceasta fr a aloca structura nsi. Dac dorii i folosii aceast
a doua metod, aceasta poate duce la anumite probleme dificil de depis Urmtoarea expunere va scoate n
eviden unele probleme de evitat.
Dac transferai un pointer funciei mysql_init (), acesta trebuie s indice spre ceva...'{ lum aceast component
de program: main()
MYSQL *conn;
mysql_init (conn);
Problema este c mysql_init () primete un pointer, dar pointerul nu indic spre : semnificativ, conn este o
variabil local i, implicit, constituie un spaiu de st neiniializat, care poate indica oriunde atunci cnd funcia
main () i ncepe execuii Aceasta nseamn c mysql_init () va folosi pointerul si va scrie undeva ntr-o zon
aleatoare de memorie. Dac avei noroc, conn va indica undeva n afara spaiului de adrese al programului
dumneavoastr i sistemul va termina imediat execuia prograft. mului, deci vei sesiza apariia problemei nc
de la primele linii de program. Dac a' suntei att de norocos, conn va indica spre date pe care le folosii mai
trziu n program^* iar dumneavoastr nu vei sesiza apariia unei probleme dect atunci cnd programai
ncearc efectiv s foloseasc datele respective, n acest caz, problema va prea c se pvoHt' duce ntr-o faz a
programului mult mai avansat dect aceea din care provine efectiv i poate fi mult mai dificil de depistat. Iat
un program cu probleme" asemntor: MYSQL *conn;
main()
Capitolul 6 Interfaa API MySQL pentru C 291
mysql_init(conn); mysql_real_connect mysql_query (conn,
(conn, ...)
"SHOW DATABASES");
n acest caz, conn este o variabil global, deci este iniializat la O (adic NULL) nainte de pornirea
programului. mysql_init () vede un argument NULL, deci se iniializeaz i aloc o nou variabil de tratare a
conexiunii. Din pcate, conn este nc NULL, deoarece niciodat nu i este atribuit vreo valoare. De ndat ce
transferai conn unei funcii din interfaa API n C pentru MySQL care impune o variabil de tratare a conexiunii
diferit de NULL, programul dumneavoastr va cdea. Pentru ambele programe, remediul l constituie
asigurarea faptului c variabila conn are o anumit valoare. De exemplu, o putei iniializa la adresa unei
structuri MYSQL deja alocate: MYSQL conn_struct, *conn = &conn_struct;
mysql_init (conn);
Totui, soluia recomandat (i mai uoar!) este pur i simplu de a transfera NULL n mod explicit funciei
mysql_init(), de a permite acelei funcii s aloce automat structura MYSQL i s atribuii variabilei conn
valoarea returnat:
MYSQL *conn;
conn = mysql_init (NULL);
n orice caz, nu uitai s testai valoarea returnat a funciei mysql_init(), pentru a v asigura c nu este NULL.
Greeala 2: Lipsa testului validitii unui set de rezultate
Nu uitai s verificai starea apelurilor de unde v ateptai s obinei un set de rezultate. Programul de mai jos
nu face asta:
MYSQL_RES *res_set;
MYSQL_ROW row;
res_set = mysql_store_result (conn); while ((row = mysql_fetch_row (res_set)) !
{
/* prelucrare rand */
NULL)
Din pcate, dac mysql_store_result() eueaz, res_set este NULL si ciclul while nici mcar nu mai trebuie
executat. Testai valoarea returnat de funciile care returneaz seturi de rezultate, pentru a v asigura c avei
efectiv un material de lucru".
Greeala 3: Ignorarea valorilor NULL din coloane
Nu uitai s verificai dac valorile coloanelor din tabloul MYSQL_ROW returnat de ctre funcia
mysql_fetch_row() sunt pointeri NULL. Pe unele calculatoare, programul urmtor cade" dac row[i] este
NULL: