Documente Academic
Documente Profesional
Documente Cultură
Mysql 300 p-7
Mysql 300 p-7
Mysql 300 p-7
despre modul de generare a listei membrilor pentru programul banchetului anual al Ligii istorice, precum i
despre modul de generare a catalogului tiprit al Ligii. Totui, DBI furnizeaz multe alte caracteristici utile.
Seciunile urmtoare trateaz despre unele din acestea ntr-un mod mai detaliat, astfel nct s putei utiliza
scripturile Perl nu numai pentru rularea unor simple instruciuni SELECT.
Tratarea erorilor
Scriptul dump_members a activat atributul de tratare a erorilor RaiseError atunci cnd a invocat metoda connect
(), astfel nct erorile s cauzeze terminarea imediat a execuiei scriptului cu un mesaj de eroare. Exist i alte
metode de tratare a erorilor. De exemplu, putei verifica personal apariia erorilor n loc de a cere programului
DBI s execute aceast operaie.
Pentru a vedea cum se poate controla modul de tratare a erorilor n DBI, s examinm mai ndeaproape
argumentul final al funciei connect(). Cele dou atribute relevante sunt RaiseError i PrintError:
Dac RaiseError este activat (adic primete o valoare diferit de zero), DBI apeleaz funcia die() pentru a
tipri un mesaj i pentru a-i termina execuia dac se produce vreo eroare ntr-o metod DBI.
Dac PrintError este activat, DBI apeleaz funcia warn() pentru a afia un mesaj atunci cnd se produce o
eroare DBI, dar scriptul i continu execuia.
n mod prestabilit, RaiseError este dezactivat i PrintError este activat, n acest caz, dac apelul la funcia
connect () eueaz, DBI afieaz un mesaj, dar i continu execuia. Astfel, folosind metoda prestabilit de
tratare a erorilor, pe care o obinei dac omitei al patrulea argument al funciei connect (), putei cuta erori n
acest mod: $dbh = DBI->connect ($dsn, $user_name, Spassword)
or exit (1);
Dac se produce vreo eroare, funcia connect () returneaz undef pentru a indica eecul, ceea ce declaneaz
apelul la exit(). Nu trebuie s afiai un mesaj de eroare, deoarece DBI va fi afiat deja unul.
Dac specificai n mod explicit valorile prestabilite ale atributelor operaiei de verificare a erorilor, apelul la
funcia connect () se prezint astfel: $dbh = DBI->connect ($dsn, $user_name, $password,
{ RaiseError => O, PrintError => 1}) or exit (1);
Este ceva mai mult de scris, dar un cititor ocazional va sesiza mai rapid care este metoda de tratare a erorilor
utilizat.
Dac dorii s cutai personal erorile si s v afiai propriile mesaje, dezactivai att RaiseError, ct si
PrintError:
$dbh = DBI->connect ($dsn, $user_name, $password,
{ RaiseError => O, PrintError => 0}) or die "Nu se poate conecta la server: $DBI::err ($DBI::errstr)\n";
Vi' >' *,-" - ' '
fr .**.'1v-*."'r
Capitolul 7 Interfaa API pentru Perl DBI 301
Variabilele $DBI : : err i $DBI : : errstr, folosite n apelul la funcia die ( ) prezentat anterior, sunt utile pentru
construcia mesajelor de eroare. Aceste variabile conin codul de eroare i irul de eroare, asemntor cu funciile
mysql_errno{), respectiv mysql_error ( ) din interfaa API pentru C.
Dac nu dorii dect ca DBI s trateze erorile n locul dumneavoastr, astfel nct s nu fie necesar s le verificai
personal, activai RaiseError:
$dbh = DBI->connect ($dsn, $user_name, Spassword, { RaiseError => 1 }); Aceasta reprezint de departe
metoda cea mai simpl i este cea folosit de scriptul dumpjnembers. Activarea atributului RaiseError poate fi
inadecvat dac dorii s executai un program personal de curenie atunci cnd scriptul i ncheie execuia,
dei n acest caz putei efectua operaiile dorite redefinind variabila de manipulare $SIG{_DIE_}.
Un alt motiv pentru care dorii s evitai activarea atributului RaiseError este acela c DBI afieaz informaii de
natur tehnic n mesajele sale, dup cum urmeaz:
disconnect (DBI: :db=HASH(Ox197aae4) ) invalidates 1 active statement. Either destroy statement handles or
call finish on them before disconnecting. (Instruciunea ... invalideaz l instruciune activ. Distrugei variabilele
de manipulare pentru instruciuni sau apelai funcia finish ( ) pentru acestea nainte de deconectare.)
Acestea constituie informaii utile pentru un programator, dar nu este genul de lucruri pe care utilizatorul
cotidian dorete s le vad. n acest caz, este mai bine s verificai personal apariia erorilor, astfel nct s putei
afia mesaje mai semnificative pentru persoanele care vor utiliza scriptul. Alternativ, putei lua n considerare
redefinirea variabilei de manipulare $SIG{ _ DIE _ }. Acest lucru poate fi util, deoarece v permite s activai
RaiseError pentru simplificarea procedurii de tratare a erorilor, dar i s nlocuii mesajele de eroare prestabilite
pe care le prezint DBI cu propriile dumneavoastr mesaje. Pentru a v furniza propria variabil de manipulare _
DIE _ , nainte de a executa vreun apel DBI, procedai astfel:
$SIG{ _ DIE _ } = sub {die "Scuze, s-a produs o eroareVn"; }; De asemenea, putei declara o subrutin n
maniera uzual i putei stabili valoarea variabilei de manipulare folosind o referin la subrutin:
sub die_handler
{
die "Scuze, s-a produs o eroare\n";
$SIG{_DIE_} = \&die_handler;
Ca alternativ la transferul n form literal al atributelor operaiei de tratare a erorilor n cadrul apelului la
funcia connect ( ), acestea pot fi definite folosind o funcie hash i transfernd o referin la hash. Unii sunt de
prere c extinderea" valorilor atributelor n acest mod faciliteaz citirea si editarea scripturilor, dar cele dou
metode sunt identice din punct de vedere funcional. Iat un exemplu care prezint modul de utilizare al valorii
hash al unui atribut:
302 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL %attr=
PrintError => O, RaiseError => O
);
$dbh = DBI->connect ($dsn, $user_name, Spassword, \%attr)
or die "Nu se poate conecta la server: $DBI::err ($DBI::errstr)\n"; Scriptul urmtor, intitulat dump_members2,
ilustreaz modul de scriere a unui script atunci cnd dorii s verificai personal apariia erorilor si s v scriei
propriile mesaje, dumpjnem-bers2 prelucreaz aceeai interogare ca i dumpjnembers, dar dezactiveaz n mod
explicit PrintError i RaiseError, dup care testeaz rezultatul fiecrui apel DBI. Cnd se produce o eroare,
scriptul invoc subrutina bail_out() care afieaz un mesaj, respectiv coninutul variabilelor $DBI:: err i $DBI::
errstr nainte de a-si ncheia execuia:
#! /usr/bin/perl
# dutnp_members2 - afieaz lista membrilor Ligii istorice
use DBI; use strict;
my ($dsn) = "DBI:mysql:samp_db:localhost"; # nume sursa date my ($user_name) = "paul"; # numele
utilizatorului my ($password) = "secret" # parola
# variabile pentru baza de date si instruciune
# tablou pentru rndurile returnate de interogare
# atributele de tratare a erorilor
my ($dbh, $sth); my (@ary); my (%attr) =
PrintError => O, RaiseError => O
# conectarea la baza de date
$dbh = DBI->connect ($dsn, $user_name, Spassword, \%attr) or bail_out ("Nu se poate conecta la baza de
date");
# emiterea interogrii
$sth = $dbh->prepare ("SELECT nume, prenume, sufix, email,
. "strada, ora, stat, cod_postal, telefon FROM membru"
"ORDER BY nume")
or bail_out ("Nu poate prepara interogarea"); $sth->execute ()
or bail_out ("Nu poate executa interogarea");
Capitolul 7 Interfaa API pentru Perl DBI 303
# citete rezultatele interogrii while (@ary = $sth->fetchrow_array())
{
print join ("\t", @ary), "\n";
}
$DBI::err == O
or bail_out ("Eroare in timpul regsirii");
# curenie $sth->finish ()
or bail_out ("Nu poate finaliza interogarea");
$dbh->disconnect ()
or bail_out ("Nu se poate deconecta de la baza de date"); exit (0);
# subrutina bail_out() - afieaz cod de eroare si irul de eroare,
apoi isi ncheie execuia
sub bail_out
my ($message) = shift;
die "$message\nError $DBI::err ($DBI::errstr)\n");
Funcia bail_out()este similar cu funcia print_error() pe care am folosit-o pentru scrierea programelor C din
capitolul 6, cu deosebirea c bail_out () i ncheie execuia, nu returneaz controlul apelantului. bail_out () v
scutete de efortul de a scrie numele variabilelor $DBI:: err i $OBI:: errstr de fiecare dat cnd dorii s scriei
un mesaj de eroare. De asemenea, prin ncapsularea afirii mesajelor ntr-o subrutin, putei schimba n mod
uniform formatul mesajelor dumneavoastr de eroare n ntreg scriptul, efectund o modificare n subrutin.
Scriptul dumpjnembers include dup ciclul de preluare a rndurilor un test pe care scriptul dumpjnembers nu-1
conine. Deoarece dump_members2 nu-i ncheie automat execuia dac se produce vreo eroare n tabloul
fetchrow_array (), este mai prudent s se determine dac ciclul s-a terminat datorit citirii complete a setului de
nregistrri (terminare normal) sau datorit producerii unei erori. Ciclul se ncheie n ambele situaii, desigur,
dar, n cazul apariiei unei erori, datele de ieire ale scriptului vor fi trunchiate, n absena unei verificri a
apariiei erorilor, persoana care ruleaz scriptul nu va avea nici cel mai mic indiciu c s-ar fi ntmplat ceva!
Dac verificai personal apariia erorilor, nu uitai s testai rezultatul ciclurilor de preluare.
304 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Tratarea interogrilor care nu returneaz nici un set de rezultate
Instruciuni precum DELETE, INSERT, REPLACE si UPDATE, care nu returneaz rnduri, sunt relativ simplu
de prelucrat n comparaie cu instruciuni precum SELECT, DESCRIBE, EXPLAIN i SHOW, care returneaz
rnduri. Pentru a prelucra o instruciune care nu este de tip SELECT, transferai-o funciei do() folosind variabila
de manipulare pentru baze de date. Metoda do( ) prepar i execut interogarea ntr-o singur etap. De exemplu,
pentru a introduce rubrica aferent unui membru nou, pe nume Cornel Vasile, cu data expirrii 3 iunie 2002,
putei proceda astfel:
$rows = $dbh->do ("INSERT membru ( nume, prenume, expirare )"
. " VALUES (' Vasile1, 'Cornel1 , '2002-6-3')");
Metoda do( ) returneaz un numr al rndurilor afectate, respectiv undef dac se produce vreo eroare. O eroare
se poate produce din diverse motive. (De exemplu, interogarea este deformat sau dumneavoastr nu avei
permisiunea de a obine acces la tabel.) Pentru o valoare returnat diferit de undef, fii atent la situaia n care nu
este afectat nici un rnd. Cnd survine o atare situaie, funcia do() nu returneaz numrul 0; n schimb,
returneaz irul "OEO" (notaia tiinific folosit n Perl pentru zero). "OEO" are valoarea O ntr-un context
numeric, dar este considerat ca adevrat" n teste condiionale, astfel nct s poat fi deosebit cu uurin de
undef. Dac funcia do( ) ar fi returnat O, atunci ar fi fost mai dificil s se fac diferena dintre apariia unei erori
(undef) i cazul n care nu a fost afectat nici un rnd. Putei verifica apariia unei erori folosind oricare din
urmtoarele teste:
if (idefined ($rows)) { # eroare }
if (!$rows) { # eroare }
n contexte numerice, "OEO" are valoarea 0. Programul urmtor va afia corect numrul rndurilor pentru orice
valoare a variabilei $rows diferit de undef:
if (!$rows)
{
prin "eroare\n";
} else
<
$rows += 0; # conversie forat la numr daca "OEO"
prin "$rows rnduri afectate\n";
} _
De asemenea, putei afia valoarea variabilei $rows folosind un format %d cu funcia, printf ( ) pentru a fora o
conversie implicit la un numr: if (!$rows) {
prin "eroare\n";
} else
print "%d $rows rnduri afectate\n", $rows;
Capitolul 7 Interfaa API pentru Perl DBI 305
Metoda do () este echivalent cu prepare (), urmat de execute (). Instruciunea INSERT anterioar poate fi
emis i astfel, nu numai prin apelarea funciei do ():
$sth = $dbh->prepare("INSERT membru (nume.prenume,expirare)" . " VALUES('Vasile','Cornel','2002-6-
3')");
$rows = $sth->execute ();
Tratarea interogrilor care returneaz un set de rezultate
Aceast seciune furnizeaz mai multe informaii despre numeroase opiuni pe care le avei la dispoziie n
vederea executrii ciclului de preluare a rndurilor pentru interogrile SELECT (sau pentru alte interogri de tip
SELECT care returneaz rnduri, precum DESCRIBE, EXPLAIN si SHOW). De asemenea, se discut despre
obinerea numrului de rnduri ale unui rezultat, modul de tratare a seturilor de rezultate pentru care nu este
necesar nici un ciclu, respectiv despre regsirea dintr-o micare" a unui ntreg set de rezultate.
Scrierea ciclurilor de preluare a rndurilor
Scriptul dumpjnembers regsea date folosind o secven standard de metode DBI: pre-pare() pentru a permite
driverului s pre-proceseze interogarea, execute() pentru a ncepe execuia interogrii, f etchrow_array () pentru
a prelua fiecare rnd al setului de rezultate, respectiv finish() pentru a elibera resursele alocate interogrii.
prepare(), execute!) i finish() sunt componente oarecum standard ale prelucrrii oricrei interogri care
returneaz rnduri. Totui, pentru preluarea rndurilor, f etchrow_array () este numai o opiune din alte
numeroase metode (vezi tabelul 7.3).
Tabelul 7.3 Metode DBI pentru preluarea rndurilor
Numele metodei
fetchrow_array()
fetchrow_arrayref()
fetch()
fetchrowjiashref()
Valoare returnat
Tablou cu valorile din rnd
Referin la un tablou cu valorile din rnd
Similar cu f etchrow_arrayref ()
Referin la valorile hash ale valorilor din rnd, care conin si numele coloanei drept cheie
Exemplele urmtoare prezint modul de utilizare a fiecrei metode de preluare a rndurilor. Exemplele parcurg
ciclic rndurile unui set de rezultate i, pentru fiecare rnd, afieaz valorile din coloane separate prin virgule.
Exist metode mai eficiente de a scrie codul de afiare n anumite situaii, dar exemplele sunt astfel scrise pentru
a ilustra sintaxa necesar accesului la valorile individuale din coloane.
f etchrow_array ( ) este folosit dup cum urmeaz: while (@ary = $sth->fetchrow_array ())
$delim =
Continuare
Este important s efectuai testele ntr-o ordine corespunztoare, deoarece att a doua, ct i a treia comparaie
sunt adevrate dac $col_val este un ir vid. Dac inversai ordinea acestor comparaii, vei identifica n mod
incorect irurile vide ca fiind zero,
Probleme legate de ghilimele
Pn acum, am construit interogri n cel mai elementar mod cu putin, folosind iruri ncadrate ntre ghilimele
simple. Acest fapt creeaz o problem la nivelul lexical al limbajului Perl, atunci cnd irurile dumneavoastr
ncadrate ntre ghilimele conin valori de asemenea ncadrate ntre ghilimele. De asemenea, putei avea probleme
la nivelul SQL, cnd dorii s inserai sau s selectai valori care conin ghilimele, backslash-uri sau date binare.
Dac specificai o interogare sub forma unui ir Perl delimitat prin ghilimele, trebuie s modificai semnificaia
tuturor apariiilor caracterului de citare n interiorul irului de interogare:
$query = 'INSERT absente VALUES(14,\'1999-9-16\')';
Squery = "INSERT absente vALUESU.Viggg-g-ieV')";
Att Perl, ct si MySQL v permit s ncadrai iruri ntre ghilimele, folosind ghilimele simple sau duble, deci
uneori putei evita modificarea semnificaiei unor caractere prin combinarea semnelor citrii:
Squery = 'INSERT absente vALUES(14,"1999-9-16")';
$query = "INSERT absente VALUES(14,'1999-9-16')";
Cu toate acestea, cele dou tipuri de ghilimele nu sunt echivalente n Perl. Referinele la variabile sunt
interpretate numai n interiorul ghilimelelor duble. Ca atare, ghilimelele simple nu sunt foarte utile atunci cnd
dorii s construii interogri prin nglobarea referinelor la variabile n irul de interogare. De exemplu, dac
valoarea variabilei $var este 14, urmtoarele dou iruri nu sunt echivalente:
"SELECT * FROM membru WHERE id = $var"
'SELECT * FROM membru WHERE id = $var'
irurile sunt interpretate dup cum urmeaz; evident, primul ir este cel care poate fi trimis unui server MySQL:
"SELECT * FROM membru WHERE id = 14"
'SELECT * FROM membru WHERE id = $var'
O alternativ la ncadrarea irurilor ntre ghilimele duble const n utilizarea construciei qq{}, care indic
programului Perl s trateze ntreg textul cuprins ntre qq{ si } ca pe un ir ncadrat ntre ghilimele duble.
(Gndii-v c qq nseamn ghilimele duble.) De exemplu, urmtoarele dou linii sunt echivalente:
$data = "1999-9-16";
$data = qq{1999-9-16};
Putei construi interogri fr a acorda prea mare atenie ghilimelelor dac folosii qq{}, deoarece putei folosi
ghilimelele (simple sau duble) n cadrul irului de interogare fr a fi necesar s le modificai semnificaia. De
asemenea, referinele la variabile sunt interpretate. Ambele proprieti ale construciei qq{} sunt prezentate n
urmtoarea interogare:
$id .= 14;
$data = "1999-9-16";
$interogare = qq{INSERT absente VALUES($id,"$data")};
Capitolul? Interfaa API pentru Perl DBI 315
Nu trebuie s folosii paranteze acolade ca delimitatori pentru construcia qq. Alte forme, precum qq() i qq/ /
sunt de asemenea utilizabile, cu condiia ca delimitatorul de nchidere s nu fie coninut n interiorul irului. Eu
prefer qq{}, deoarece este mai puin probabil ca paranteza acolad s apar n textul irului interogrii dect
paranteza. rotund sau caracterul /, dup cum este mai puin probabil s fie confundat cu finalul irului de
interogare dect ultimele dou caractere. De exemplu, paranteza rotund apare n cadrul instruciunii INSERT
prezentate anterior, deci qq() nu este o construcie util pentru ncadrarea ntre ghilimele a irului interogrii.
Construcia qq{} se poate extinde pe mai multe rnduri, ceea ce este util dac dorii ca irul interogrii s ias n
eviden n raport cu liniile de program Perl nconjurtoare:
$id = U;
$data = "1999-9-16";
Sinterogare = qq{
{INSERT absente VALUES($id,"$data")
};
Acest lucru este de asemenea util dac dorii pur i simplu s formatai interogarea pe linii multiple, pentru a o
face mai uor de citit. De exemplu, instruciunea SELECT din scriptul dumpjnembers se prezint astfel:
$sth = $dbh->prepare ("SELECT nume, prenume, sufix, email," . "strada, ora, stat, cod_postal, telefon FROM
membru" . "ORDER BY nume");
Folosind construcia qq{}, interogarea poate fi scris astfel: $sth = $dbh->prepare (qq{ SELECT
nume, prenume, sufix, email, strada, ora, stat, cod_postal, telefon FROM membru ORDER BY nume
});
Este la fel de adevrat c irurile ncadrate ntre ghilimele duble se pot i ele extinde pe mai multe linii. Dar eu
prefer mai mult construcia qq{} pentru scrierea de iruri pe linii multiple. Atunci cnd gsesc ntr-o linie nite
ghilimele duble fr corespondent, prima mea reacie este: Nu cumva e o eroare de sintax?" Apoi, pierd
vremea cutnd ghilimelele corespondente.
Construcia qq{} se ocup de problemele legate de ghilimele la nivelul lexical al limbajului Perl, astfel nct s
putei insera cu uurin ghilimele ntr-un ir, fr ca Perl s protesteze. Totui, trebuie s v gndii i la sintaxa
la nivel SQL. S lum n considerare aceast ncercare de a insera o nregistrare n tabelul membru:
$nume = "O'Malley";
$prenume = "Brian";
$data_expirare = "2002-9-1";
$rows = $dbh->do (qq{
Continuare
316 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
INSERT membru (nume,prenume,data_expirare) VALUES('$nume','Sprenume','$data_expirare')
});
irul pe care funcia do () l trimite serverului MySQL se prezint astfel:
INSERT membru (nume,prenume,data_expirare)
VALUESCO'Malley' , 'Brian', '2002-9-1')
Aceasta nu este o interogare SQL corect, deoarece un caracter de tip ghilimele simple apare n interiorul unui
ir delimitat prin ghilimele simple. Am ntlnit o problem similar n capitolul 6. Acolo, am rezolvat problema
cu ajutorul funciei mysql_escape_string(). DBI furnizeaz un mecanism similar: pentru fiecare valoare cu
ghilimele pe care dorii s o folosii literal ntr-o instruciune, apelai metoda quote () si folosii n schimb
valoarea returnat de aceasta.
Exemplul anterior poate fi scris ntr-un mod mai adecvat astfel: $nume = $dbh->quote ("O'Malley"); $prenume
= $dbh->quote ("Brian"); $data_expirare = $dbh->quote ("2002-9-1'); $rows = $dbh->do (qq{
INSERT membru (nume,prenume,data_expirare) VALUES($nume,$prenume,$data_expirare)
});
Acum, irul pe care funcia do() l trimite serverului MySQL se prezint astfel, ghilimelele care apar n interiorul
irului fiind camuflate" adecvat pentru server:
INSERT membru (nume,prenume,data_expirare)
VALUES('OVMalley', 'Brian1 ,'2002-9-1')
Observai c, atunci cnd facei referire la $nume i $prenume n irul interogrii, nu adugai ghilimele de
delimitare; metoda quote () le furnizeaz automat. Dac adugai ghilimele, interogarea dumneavoastr va avea
prea multe ghilimele, aa cum se poate vedea n exemplul urmtor:
$value = "paul";
$quoted_value = $dbh->quote ($value);
prin " ... WHERE nume = $quoted_value\n"; prin " ... WHERE nume = '$quoted_value'\n"; Aceste
instruciuni furnizeaz urmtoarele date de ieire: ... WHERE nume = 'paul' ... WHERE nume = ''paul''
Cmpuri de nlocuire i asocierea parametrilor
n seciunile anterioare, am construit interogri prin plasarea valorilor ce urmau a fi inserate n baza de date sau
folosite drept criterii de selecie direct n irul interogrii. Acest lucru nu este necesar. DBI v permite s plasai
ntr-un ir de interogare indica-
Capitolul? Interfaa API pentru Peri DBI 317
toare speciale, denumite cmpuri de nlocuire, iar apoi s furnizai, la executarea interogrii, valorile ce vor fi
folosite n locul acestor indicatoare. Principalul motiv pentru aceast operaie l constituie mbuntirea
performanelor, mai ales atunci cnd executai n mod repetat o interogare, n cadrul unui ciclu.
Ca o ilustrare a modului de lucru a cmpurilor de nlocuire, s presupunem c ncepei un nou semestru la coal
i dorii s tergei tabelul elev pentru catalogul dumneavoastr si apoi s-1 iniializai cu noii elevi, folosind o
list a numelor elevilor incluse ntr-un fiier, n lipsa cmpurilor de nlocuire, putei terge coninutul curent al
tabelului i ncrcai noile nume astfel:
$dbh->do (qq{ DELETE FROM elev } ); # terge rndurile existente while (<>)
# adaug rnduri noi
{
chomp;
$_ = $dbh->quote ($_);
$dbh->do (qq{ INSERT elev SET nume = $_}); }
Acest procedeu este ineficient, deoarece forma elementar a interogrii INSERT este aceeai de fiecare dat, iar
funcia do() apeleaz prepare () i execute () de fiecare dat pe parcursul ciclului. De asemenea, este mai
eficienta apelarea funciei prepare () o singur dat, pentru a configura instruciunea INSERT nainte de a
introduce ciclul, precum si invocarea funciei execute () o singur dat n cadrul ciclului. Astfel, se evit toate
invocrile funciei prepare (), cu o singur excepie. DBI ne permite acest lucru, astfel: $dbh->do (qq{ DELETE
FROM elev } ); # terge rndurile existente $sth = $dbh->prepare (qq{ INSERT elev SET nume = ? });
while (<>) # adaug rnduri noi
{
chomp; $sth->execute ($_);
} $sth->finish();
Observai semnul ntrebrii din interogarea INSERT. Acela este cmpul de nlocuire. Cnd este invocat funcia
execute (), transferai valoarea care va lua locul cmpului de nlocuire atunci cnd interogarea este trimis la
server, n general, dac apelai funcia do () n cadrul unui ciclu, este mai bine s invocai prepare () anterior
ciclului, respectiv execute() n interiorul acestuia.
Cteva aspecte de reinut despre cmpurile de nlocuire:
Nu delimitai caracterul cmp de nlocuire cu ghilimele n interiorul irului interogrii. Dac o facei, acesta nu
va fi recunoscut drept cmp de nlocuire.
Nu folosii metoda quote () pentru a specifica valorile cmpului de nlocuire; n caz contrar, vei obine
ghilimele suplimentare n valorile pe care le inserai.
ntr-un ir al unei interogri pot exista mai multe cmpuri de nlocuire, dar nu uitai s transmitei funciei
execute ()un numr de valori egal cu indicatoarele pentru cmpurile de nlocuire.
Dup ce invocai prepare () i execute() ca de obicei, putei obine numele coloanelor din @{$sth->{NAME}}.
Reinei, totui, c dei acest mic truc de utilizare a unei interogri vide" funcioneaz pentru MySQL, nu este
portabil i nu se poate aplica tuturor motoarelor de baze de date.
Pentru mai multe informaii privind atributele furnizate de DBI i de DBD::mysql, consultai Anexa G. Este la
latitudinea dumneavoastr s determinai dac dorii s obinei un maxim de portabilitate evitnd atributele
specifice sistemului MySQL sau s beneficiai de ele, cu preul reducerii portabilitii.
Utilizarea DBI
Pn acum ai vzut un numr de concepte implicate n programarea DBI, deci s trecem la unele din lucrurile pe
care doream s le putem face n ceea ce privete baza noastr de date demonstrativ. Scopurile noastre au fost
schiate la nceput n capitolul l, Introducere n MySQL i SQL". Cele care vor fi abordate prin scrierea
scripturilor DBI din capitolul de fa sunt enumerate aici.
Pentru proiectul de eviden a rezultatelor colare, dorim s putem regsi punctaje pentru orice chestionar sau
test dat.
Pentru Liga istoric, dorim s putem executa urmtoarele operaii:
Generarea catalogului membrilor n diferite formate. Dorim o list numai cu nume, care va fi utilizat n
programul editat cu ocazia banchetului anual, ntr-un format care se poate folosi pentru generarea catalogului
tiprit.
Identificarea acelor membri ai Ligii care trebuie s-i plteasc n curnd cotizaia, urmat de trimiterea unui
mesaj e-mail pe adresa acestora, pentru a-i anuna.
Editarea intrrilor aferente membrilor. (La urma urmelor, va trebui s le actualizm datele de expirare dup ce
acetia i achit cotizaiile.)
Identificarea membrilor cu interese comune.
Publicarea catalogului pe Internet.
Pentru unele din aceste sarcini, vom scrie scripturi care ruleaz de la linia de comand. Pentru celelalte, vom crea
scripturi n seciunea urmtoare, Utilizarea DBI n aplicaiile Web", pe care le putei folosi n conjuncie cu
serverul dumneavoastr de Web. La sfritul capitolului, vom mai rmne cu un numr de sarcini nendeplinite.
Le vom rezolva i pe acestea n capitolul 8, Interfaa API pentru PHP".
Generarea catalogului Ligii istorice
Unul dintre scopurile noastre este de a genera informaii din catalogul Ligii istorice n diferite formate. Cel mai
simplu format pe care l vom genera este o list cu numele membrilor pentru programul banchetului anual.
Aceasta poate fi un listing n format text simplu. Lista va deveni o parte a documentului mai mare folosit pentru
a crea programul, deci tot ceea ne trebuie este o entitate care poate fi lipit" n documentul respectiv. Pentru
catalogul n format acceptabil pentru tipar, este necesar o reprezentare mai adecvat dect aceea n format text
simplu, deoarece dorim un text cu o formatre estetic. O opiune rezonabil aici este RTF (Rich Text Format),
un format creat de Microsoft i
Capitolul 7 Interfaa API pentru Perl DBI 331
acceptat de numeroase procesoare de text. Word este un asemenea program, desigur, dar RTF este acceptat i de
multe alte programe, precum WordPerfect i AppleWorks. Diferitele procesoare de text accept RTF n moduri
diferite, dar vom folosi un subset elementar al specificaiei RTF complete, care trebuie s fie acceptat de orice
procesor de texte care prezint chiar i cel mai redus nivel posibil de compatibilitate cu RTF.
Procedurile pentru generarea formatelor de catalog de tip list pentru banchet, respectiv RTF, sunt n esen
aceleai: emitei o interogare pentru regsirea intrrilor si apoi rulai un ciclu care preia si formateaz fiecare
intrare. Datorit acestei asemnri fundamentale, ar fi interesant s evitm scrierea a dou scripturi diferite, n
acest sens, s scriem un singur script, intitulat gen_cat, care poate genera date de ieire din catalog n diferite
formate. Putem structura scriptul dup cum urmeaz:
1. nainte de a scrie coninutul intrrilor, efectuai toate iniializrile necesare pentru formatul datelor de ieire.
Pentru lista membrilor care se va insera n programul pentru banchet nu este necesar nici o iniializare special,
dar va trebui s scriem un limbaj de control iniial pentru versiunea RTF.
2. Preluai i afiai fiecare intrare, formatat adecvat pentru tipul datelor de ieire dorit.
3. Dup ce toate intrrile au fost prelucrate, efectuai toate operaiile necesare de curenie i de terminare a
programului. Din nou, pentru lista de la banchet nu este nevoie de operaii de manipulare speciale, dar pentru
versiunea RTF este necesar un limbaj de control al nchiderii.
n viitor, vom folosi acest script pentru a scrie date de ieire n alte formate, deci l vom face extensibil prin
configurarea unei cutii de distribuie" - o combinaie cu un element pentru fiecare format al datelor de ieire.
Fiecare element specific funcii care genereaz date de ieire n mod adecvat pentru un format dat: o funcie de
iniializare, o funcie de scriere a intrrilor i o funcie de curenie:
# cutie de distribuie care conine funcii de formatare
# pentru fiecare format al datelor de ieire my (%cutie_distributie) =
"banquet" =>
# funcii pentru lista de banchet
"init" => undef, # nu este necesara nici
# o iniializare
"entry" => \&format_banquet_entry, "cleanup" => undef # curenia nu este necesara
},
"rtf" =>
{
"init" => \&rtf_init,
"entry" => V&format_rtf_entry,
"cleanup" => \&rtf_cleanup }
# funcii pentru formatul RTF
sub col_prompt
{
my ($name, $val, $show_current) = @_;
my ($prompt, $str);
Sprompt = $name;
Sprompt .= " [$val]" if $show_current;
Sprompt .= ": ";
print STDERR $prompt;
chomp ($str = <STDIN>);
return ($str ? $str : $val); }
Motivul pentru care funcia col_prompt() preia un argument $show_current este acela c vom folosi aceast
funcie i pentru solicitarea valorilor coloanelor din intrrile existente aferente membrilor atunci cnd scripul
este utilizat pentru actualizarea unei intrri. $show_current va fi O cnd crem o intrare nou, deoarece nu exist
valori curente de afiat, n acest din urm caz, promptul va afia valoarea curent, pe care utilizatorul o poate
accepta, pur si simplu, prin apsarea tastei Enter. Programul pentru editarea intrrii aferente unui membru
existent este similar cu aceea j pentru crearea unui membru nou. Totui, avem o intrare de lucru, deci rutina de
soliei-: tare afieaz valorile intrrii curente, iar funcia edit_member() emite o instruciune\ UPDATE, nu o
instruciune INSERT:
# editeaz coninutul curent al unei intrri
sub editjnember
my ($col_name_ref , $entry_ref) my ($col_val, Squery, $delim);
Capitolul 7 Interfaa API pentru Perl DBI 347
# afieaz valorile iniiale, cere permisiunea de a continua
# si de a edita
showjnember ($col_name_ref, $entry_ref);
return unless prompt ("\nEditez aceasta intrare? ") =- /Ay/i;
# solicita valori noi; utilizatorul tasteaz valoarea noua
# pentru a inlocui valoarea existenta, "nuli" pentru a
# introduce o valoare NULL, "exit" pentru a parai prematur
# programul, respectiv Enter pentru a accepta valoarea existenta, foreach my $col_name (@{$col_name_ref})
next if $col_name eq "membru_id"; # ignora cmpul cheie $col_val = $entry_ref->{$col_name}; $col_val =
"NULL" unless defined ($col_val); $col_val = col_prompt ($col_name, $col_val, 1); return if $col_val =-
/*exit$/i; # inchidere prematura $col_val = undef if $col_val =- /*null$/i; $entry_ref->{$col_name} = $col_val;
# afieaz valorile noi, cere confirmare inainte de actualizare showjnember ($coljiamej~ef, $entryj~ef);
return unless prompt ("\nActualizez aceasta intrare? ") =- /Ay/i;
# construiete o interogare UPDATE, iar apoi o emite. $query = "UPDATE membru";
$delim = " SET "; # pune "SET" inaintea primei coloane, ","
# inaintea celorlalte foreach my $coljiame (@{coljiamej*ef})
next if Scoljiame eq "membru_id"; # ignora cmpul cheie
# funcia quote() citeaz undef drept cuvntul NULL
# (fr ghilimele), adic exact ceea ce dorim.
$ query .= sprintf ("%s %s=%s", $delim, Scoljiame,
$dbh->quote ($entryj~ef->{$coljiame})); $delim = ",";
Squery = " WHERE membru_id = ?";
warn "Avertisment: intrarea nu a fost actualizat?\n"
unless $dbh->do ($query, undef, $entry_ref->{membru_id}) == 1;
348 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
O problem a funciei editjnember este aceea c nu execut validri ale valorilor de intrare. Pentru majoritatea
cmpurilor din tabelul membru, nu prea avei ce valida - acestea nu sunt nimic altceva dect cmpuri ir. Dar
trebuie verificate valorile datelor de intrare pentru coloana data_expirare, pentru a exista sigurana c sunt date si
nu altceva, ntr-o aplicaie de uz general pentru introducerea datelor, probabil c vei dori s extrageri informaii
despre un tabel, pentru a determina tipurile tuturor coloanelor sale. Apoi, putei s formulai restricii de validare
pe baza acestor tipuri, ceea ce este prea complicat pentru ceea ce vreau eu s prezint aici, deci voi aduga un
artificiu rapid la funcia col_prompt () pentru a verifica formatul datelor de intrare, n cazul n care numele
coloanei este "data_expirare". O verificare minimal a valorii datei se poate face astfel:
sub col_prompt
{
my ($name, $val, $show_current) = _;
my ($prompt, $str);
if $show_current;
loop:
Sprompt = $name;
$prompt .= " [$val]
Sprompt .= ": ";
print STDERR $prompt;
chomp ($str = <STDIN>);
# executa o verificare rudimentara a datei de expirare
if ($str && $nume eq "data_expirare")
# verifica formatul
# datei de expirare
$str =- r\d+r\d]\d+r\d]\d+$/ or goto loop;
return ($str ? $str : $val);
Modelul testeaz cele trei secvene de cifre separate prin caractere non-cifrice. Aceasta este numai o verificare
parial, deoarece nu detecteaz valori precum "1999-14-92" ca fiind incorecte. Pentru a mbunti scriptul,
putei insera verificri mai riguroase ale datelor i alte verificri, precum obligativitatea existenei unor valori
nevide n cmpurile care conin numele i prenumele.
Alte mbuntiri pot consta din omiterea actualizrii dac nu exist coloane modificate; respectiv de a anuna
utilizatorul dac nregistrarea a mai fost modificat de altcineva n timp ce utilizatorul respectiv o edita. Putei
face aceasta salvnd valorile originale ale coloanelor care compun intrrile membrilor si apoi scriind
instruciunea UPDATE pentru a actualiza numai acele coloane care s-au modificat. Dac nu s-a modificat nici o
coloan, instruciunea nici mcar nu mai trebuie emis. De asemenea, se poate scrie clauza WHERE astfel nct
s includ AND nume_coloana = valoare_coloana pentru fiecare valoare original din coloan. Acest procedeu
va cauza eecul instruciunii UPDATE dac