Perl Issuing Queries and Retrieving Results

Dont Shoot Yourself in the Foot: Check for Errors Apparent ly, t he principle t hat you should check for errors is not so obvious or widely appreciat ed as one m ight hope. Many m essages post ed on MySQL- relat ed m ailing list s are request s for help wit h program s t hat fail for reasons unknown t o t he people t hat wrot e t hem . I n a surprising num ber of cases, t he reason t hese developers are m yst ified by t heir program s is t hat t hey put in no error checking, and t hus gave t hem selves no way t o know t hat t here was a problem or t o find out what it was You cannot help yourself t his way. Plan for failure by checking for errors so t hat you can t ake appropriat e act ion if t hey occur. Now were ready t o see how t o issue queries in each API . Not e t hat alt hough t he script s check for errors as necessary, for brevit y t hey j ust print a generic m essage t hat an error occurred. You can display m ore specific error m essages using t he t echniques illust rat ed in Recipe 2.3 .

2.5.5 Perl

The Perl DBI m odule provides t wo basic approaches t o query execut ion, depending on whet her or not you expect t o get back a result set . To issue a query such as INSERT or UPDATE t hat ret urns no result set , use t he do m et hod. I t execut es t he query and ret urns t he num ber of rows affect ed by t he query, or undef if an error occurs. For exam ple, if Fred get s a new kit t y, t he following query can be used t o increm ent his cats count by one: my count = dbh-do UPDATE profile SET cats = cats+1 WHERE name = Fred; if count print row count if no error occurred { count += 0; print count rows were updated\n; } I f t he query execut es successfully but affect s no rows, do ret urns a special value, t he st r ing 0E0 t hat is, t he value zero in scient ific not at ion . 0E0 can be used for t est ing t he execut ion st at us of a query because it is t rue in Boolean cont ext s unlike undef . For successful queries, it can also be used when count ing how m any rows were affect ed, because it is t reat ed as t he num ber zero in num eric cont ext s. Of course, if you print t hat value as is, youll print 0E0 , which m ight look kind of weird t o people who use your program . The preceding exam ple shows one way t o m ake sure t his doesnt happen: adding zero t o t he value explicit ly coerces it t o num eric form so t hat it displays as . You can also use printf w it h a d form at specifier t o cause an im plicit num eric conversion: my count = dbh-do UPDATE profile SET color = color WHERE name = Fred; if count print row count if no error occurred { printf d rows were updated\n, count; } I f RaiseError is enabled, your script will t erm inat e aut om at ically if a DBI - relat ed error occurs and you dont need t o bot her checking count t o see if do failed: my count = dbh-do UPDATE profile SET color = color WHERE name = Fred; printf d rows were updated\n, count; To process queries such as SELECT t hat do ret urn a result set , use a different approach t hat involves four st eps: • Specify t he query by calling prepare using t he dat abase handle. prepare ret urns a st at em ent handle t o use wit h all subsequent operat ions on t he query. I f an error occurs, t he script t erm inat es if RaiseError is enabled; ot herwise, prepare ret urns undef . • Call execute t o execut e t he query and generat e t he result set . • Perform a loop t o fet ch t he rows ret urned by t he query. DBI provides several m et hods you can use in t his loop, which well describe short ly. • Release resources associat ed wit h t he result set by calling finish . The following exam ple illust rat es t hese st eps, using fetchrow_array as t he row - fet ching m et hod and assum ing RaiseError is enabled: my sth = dbh-prepare SELECT id, name, cats FROM profile; sth-execute ; my count = 0; while my val = sth-fetchrow_array { print id: val[0], name: val[1], cats: val[2]\n; ++count; } sth-finish ; print count rows were returned\n; The row - fet ching loop j ust shown is followed by a call t o finish , w hich closes t he result set and t ells t he server t hat it can free any resources associat ed wit h it . You dont act ually need t o call finish if you fet ch every row in t he set , because DBI not ices when youve reached t he last row and releases t he set for it self. Thus, t he exam ple could have om it t ed t he finish call wit hout ill effect . I t s m ore im port ant t o invoke finish explicit ly if you fet ch only part of a result set . The exam ple illust rat es t hat if you want t o know how m any rows a result set cont ains, you should count t hem yourself while youre fet ching t hem . Do not use t he DBI rows m et hod for t his purpose; t he DBI docum ent at ion discourages t his pract ice. The reason is t hat it is not necessarily reliable for SELECT st at em ent s—not because of som e deficiency in DBI , but because of differences in t he behavior of various dat abase engines. DBI has several funct ions t hat can be used t o obt ain a row at a t im e in a row- fet ching loop. The one used in t he previous exam ple, fetchrow_array , r et urns an array cont aining t he next row, or an em pt y list when t here are no m ore rows. Elem ent s of t he array are accessed as val[0] , val[1] , ..., and are present in t he array in t he sam e order t hey are nam ed in t he SELECT st at em ent . This funct ion is m ost useful for queries t hat explicit ly nam e colum ns t o select ed. I f you ret rieve colum ns wit h SELECT , t here are no guarant ees about t he posit ions of colum ns wit hin t he array. fetchrow_arrayref is like fetchrow_array , except t hat it ret urns a reference t o t he array, or undef when t here are no m ore rows. Elem ent s of t he array are accessed as ref-[0] , ref-[1] , and so fort h. As wit h fetchrow_array , t he values are present in t he order nam ed in t he query: my sth = dbh-prepare SELECT id, name, cats FROM profile; sth-execute ; my count = 0; while my ref = sth-fetchrow_arrayref { print id: ref-[0], name: ref-[1], cats: ref-[2]\n; ++count; } print count rows were returned\n; fetchrow_hashref ret urns a reference t o a hash st ruct ure, or undef when t here are no m ore rows: my sth = dbh-prepare SELECT id, name, cats FROM profile; sth-execute ; my count = 0; while my ref = sth-fetchrow_hashref { print id: ref-{id}, name: ref-{name}, cats: ref-{cats}\n; ++count; } print count rows were returned\n; The elem ent s of t he hash are accessed using t he nam es of t he colum ns t hat are select ed by t he query ref-{id} , ref-{name} , and so fort h . fetchrow_hashref is part icularly useful for SELECT queries, because you can access elem ent s of rows wit hout knowing anyt hing about t he order in which colum ns are ret urned. You j ust need t o know t heir nam es. On t he ot her hand, it s m ore expensive t o set up a hash t han an array, so fetchrow_hashref is slow er t han fetchrow_array or fetchrow_arrayref . I t s also possible t o lose row elem ent s if t hey have t he sam e nam e, because colum n nam es m ust be unique. The following query select s t wo values, but fetchrow_hashref would r et ur n a hash st ruct ure cont aining a single elem ent nam ed id : SELECT id, id FROM profile To avoid t his problem , you can use colum n aliases t o ensure t hat like- nam ed colum ns have dist inct nam es in t he result set . The following query ret rieves t he sam e colum ns as t he previous query, but gives t hem t he dist inct nam es id and id2 : SELECT id, id AS id2 FROM profile Adm it t edly, t his query is pret t y silly, but if youre ret rieving colum ns from m ult iple t ables, you m ay very easily run int o t he problem of having colum ns in t he result set t hat have t he sam e nam e. An exam ple where t his occurs m ay be seen in Recipe 12.4 . I n addit ion t o t he m et hods for perform ing t he query execut ion process j ust described, DBI provides several high- level ret rieval m et hods t hat issue a query and ret urn t he result set in a single operat ion. These all are dat abase handle m et hods t hat t ake care of creat ing and disposing of t he st at em ent handle int ernally before ret urning t he result set . Where t he m et hods differ is t he form in which t hey ret urn t he result . Som e ret urn t he ent ire result set , ot hers ret urn a single row or colum n of t he set , as sum m arized in t he following t able: [ 5] [ 5] selectrow_arrayref and selectall_hashref require DBI 1.15 or newer. selectrow_hashref requires DBI 1.20 or newer it was present a few versions before t hat , but wit h a different behavior t han it uses now . M e t h od Re t u r n va lu e selectrow_array First row of result set as an array selectrow_arrayref First row of result set as a reference t o an array selectrow_hashref First row of result set as a reference t o a hash selectcol_arrayref First colum n of result set as a reference t o an array selectall_arrayref Ent ire result set as a reference t o an array of array references selectall_hashref Ent ire result set as a reference t o a hash of hash references Most of t hese m et hods ret urn a reference. The except ion is selectrow_array , w hich select s t he first row of t he result set and ret urns an array or a scalar, depending on how you call it . I n array cont ext , selectrow_array ret urns t he ent ire row as an array or t he em pt y list if no row was select ed . This is useful for queries from which you expect t o obt ain only a single row: my val = dbh-selectrow_array SELECT name, birth, foods FROM profile WHERE id = 3; When selectrow_array is called in array cont ext , t he ret urn value can be used t o det erm ine t he size of t he result set . The colum n count is t he num ber of elem ent s in t he array, and t he row count is 1 or 0: my ncols = val; my nrows = ncols ? 1 : 0; You can also invoke selectrow_array in scalar cont ext , in which case it ret urns only t he first colum n from t he row. This is especially convenient for queries t hat ret urn a single value: my buddy_count = dbh-selectrow_array SELECT COUNT FROM profile; I f a query ret urns no result , selectrow_array ret urns an em pt y array or undef , depending on whet her you call it in array or scalar cont ext . selectrow_arrayref and selectrow_hashref select t he first row of t he result set and ret urn a reference t o it , or undef if no row was select ed. To access t he colum n values, t reat t he reference t he sam e way you t reat t he ret urn value from fetchrow_arrayref or fetchrow_hashref . You can also use t he reference t o get t he row and colum n count s: my ref = dbh-selectrow_arrayref query; my ncols = defined ref ? {ref} : 0; my nrows = ncols ? 1 : 0; my ref = dbh-selectrow_hashref query; my ncols = defined ref ? keys {ref} : 0; my nrows = ncols ? 1 : 0; Wit h selectcol_arrayref , a reference t o a single- colum n array is ret urned, represent ing t he first colum n of t he result set . Assum ing a non- undef ret urn value, elem ent s of t he array are accessed as ref-[ i ] for t he value from row i . The num ber of rows is t he num ber of elem ent s in t he array, and t he colum n count is 1 or 0: my ref = dbh-selectcol_arrayref query; my nrows = defined ref ? {ref} : 0; my ncols = nrows ? 1 : 0; selectall_arrayref ret urns a reference t o an array, where t he array cont ains an elem ent for each row of t he result . Each of t hese elem ent s is a reference t o an array. To access row i of t he result set , use ref-[ i ] t o get a reference t o t he row. Then t reat t he row reference t he sam e way as a ret urn value from fetchrow_arrayref t o access individual colum n values in t he row. The result set row and colum n count s are available as follows: my ref = dbh-selectall_arrayref query; my nrows = defined ref ? {ref} : 0; my ncols = nrows ? {ref-[0]} : 0; selectall_hashref is som ewhat sim ilar t o selectall_arrayref , but ret urns a reference t o a hash, each elem ent of which is a hash reference t o a row of t he result . To call it , specify an argum ent t hat indicat es which colum n t o use for hash keys. For exam ple, if youre ret rieving rows from t he profile t able, t he PRIMARY KEY is t he id colum n: my ref = dbh-selectall_hashref SELECT FROM profile, id; Then access rows using t he keys of t he hash. For exam ple, if one of t he rows has a key colum n value of 12, t he hash reference for t he row is accessed as ref-{12} . That value is keyed on colum n nam es, which you can use t o access individual colum n elem ent s for exam ple, ref-{12}-{name} . The result set row and colum n count s are available as follows: my keys = defined ref ? keys {ref} : ; my nrows = scalar keys; my ncols = nrows ? keys {ref-{keys[0]}} : 0; The selectall_ XXX m et hods are useful when you need t o process a result set m ore t han once, because DBI provides no way t o rewind a result set . By assigning t he ent ire result set t o a variable, you can it erat e t hrough it s elem ent s as oft en as you please. Take care when using t he high- level m et hods if you have RaiseError disabled. I n t hat case, a m et hods ret urn value m ay not always allow you t o dist inguish an error from an em pt y result set . For exam ple, if you call selectrow_array in scalar cont ext t o ret rieve a single value, an undef ret urn value is part icularly am biguous because it m ay indicat e any of t hree t hings: an error, an em pt y result set , or a result set consist ing of a single NULL value. I f you need t o t est for an error, you can check t he value of DBI::errstr or DBI::err . 2.5.6 PHP PHP doesnt have separat e funct ions for issuing queries t hat ret urn result set s and t hose t hat do not . I nst ead, t here is a single funct ion mysql_query for all queries. mysql_query t akes a query st ring and an opt ional connect ion ident ifier as argum ent s, and ret urns a result ident ifier. I f you leave out t he connect ion ident ifier argum ent , mysql_query uses t he m ost recent ly opened connect ion by default . The first st at em ent below uses an explicit ident ifier; t he second uses t he default connect ion: result_id = mysql_query query, conn_id; result_id = mysql_query query; I f t he query fails, result_id w ill be FALSE . This m eans t hat an error occurred because your query was bad: it was synt act ically invalid, you didnt have perm ission t o access a t able nam ed in t he query, or som e ot her problem prevent ed t he query from execut ing. A FALSE ret urn value does not m ean t hat t he query affect ed 0 rows for a DELETE , INSERT , or UPDATE or ret urned rows for a SELECT . I f result_id is not FALSE , t he query execut ed properly. What you do at t hat point depends on t he t ype of query. For queries t hat dont ret urn rows, result_id w ill be TRUE , and t he query has com plet ed. I f you want , you can call mysql_affected_rows t o find out how m any rows were changed: result_id = mysql_query DELETE FROM profile WHERE cats = 0, conn_id; if result_id die Oops, the query failed; print mysql_affected_rows conn_id . rows were deleted\n; mysql_affected_rows t akes t he connect ion ident ifier as it s argum ent . I f you om it t he argum ent , t he current connect ion is assum ed. For queries t hat ret urn a result set , mysql_query ret urns a nonzero result ident ifier. Generally, you use t his ident ifier t o call a row- fet ching funct ion in a loop, t hen call mysql_free_result t o release t he result set . The result ident ifier is really not hing m ore t han a num ber t hat t ells PHP which result set youre using. This ident ifier is not a count of t he num ber of rows select ed, nor does it cont ain t he cont ent s of any of t hose rows. Many beginning PHP program m ers m ake t he m ist ake of t hinking mysql_query ret urns a row count or a result set , but it doesnt . Make sure youre clear on t his point and youll save yourself a lot of t rouble. Heres an exam ple t hat show s how t o run a SELECT query and use t he result ident ifier t o fet ch t he rows: result_id = mysql_query SELECT id, name, cats FROM profile, conn_id; if result_id die Oops, the query failed; while row = mysql_fetch_row result_id print id: row[0], name: row[1], cats: row[2]\n; print mysql_num_rows result_id . rows were returned\n; mysql_free_result result_id; The exam ple dem onst rat es t hat you obt ain t he rows in t he result set by execut ing a loop in w hich you pass t he result ident ifier t o one of PHPs row- fet ching funct ions. To obt ain a count of t he num ber of rows in a result set , pass t he result ident ifier t o mysql_num_rows . When t here are no m ore rows, pass t he ident ifier t o mysql_free_result t o close t he r esult set . Aft er you call mysql_free_result , dont t ry t o fet ch a row or get t he row count , because at t hat point result_id is no longer valid. Each PHP row- fet ching funct ion ret urns t he next row of t he result set indicat ed by result_id , or FALSE when t here are no m ore rows. Where t hey differ is in t he dat a t ype of t he ret urn value. The funct ion shown in t he preceding exam ple, mysql_fetch_row , ret urns an array whose elem ent s correspond t o t he colum ns select ed by t he query and are accessed using num eric subscript s. mysql_fetch_array is like mysql_fetch_row , but t he array it ret urns also cont ains elem ent s t hat can be accessed using t he nam es of t he select ed colum ns. I n ot her words, you can access each colum n using eit her it s num eric posit ion or it s nam e: result_id = mysql_query SELECT id, name, cats FROM profile, conn_id; if result_id die Oops, the query failed; while row = mysql_fetch_array result_id { print id: row[0], name: row[1], cats: row[2]\n; print id: row[id], name: row[name], cats: row[cats]\n; } print mysql_num_rows result_id . rows were returned\n; mysql_free_result result_id; Despit e what you m ight expect , mysql_fetch_array is not appreciably slower t han mysql_fetch_row , even t hough t he array it ret urns cont ains m ore inform at ion. The previous exam ple does not quot e t he non- num eric elem ent nam es because t hey appear inside a quot ed st ring. Should you refer t o t he elem ent s out side of a st ring, t he elem ent nam es should be quot ed: printf id: s, name: s, cats: s\n, row[id], row[name], row[cats]; mysql_fetch_object ret urns an obj ect having m em bers t hat correspond t o t he colum ns select ed by t he query and t hat are accessed using t he colum n nam es: result_id = mysql_query SELECT id, name, cats FROM profile, conn_id; if result_id die Oops, the query failed; while row = mysql_fetch_object result_id print id: row-id, name: row-name, cats: row-cats\n; print mysql_num_rows result_id . rows were returned\n; mysql_free_result result_id; PHP 4.0.3 adds a fourt h row- fet ching funct ion, mysql_fetch_assoc , t hat ret urns an array cont aining elem ent s t hat are accessed by nam e. I n ot her words, it is like mysql_fetch_array , except t hat t he row does not cont ain t he values accessed by num eric index. Dont Use count to Get a Column Count in PHP 3 PHP program m ers som et im es fet ch a result set row and t hen use countrow t o det erm ine how m any values t he row cont ains. I t s preferable t o use mysql_num_fields inst ead, as you can see for yourself by execut ing t he following fragm ent of PHP code: if result_id = mysql_query SELECT 1, 0, NULL, conn_id die Cannot issue query\n; count = mysql_num_fields result_id; print The row contains count columns\n; if row = mysql_fetch_row result_id die Cannot fetch row\n; count = count row; print The row contains count columns\n; I f you run t he code under PHP 3, youll find t hat count ret urns 2. Wit h PHP 4, count ret urns 3. These differing result s occur because count count s array values t hat correspond t o NULL values in PHP 4, but not in PHP 3. By cont rast , mysql_field_count uniform ly ret urns 3 for bot h versions of PHP. The m oral is t hat count wont necessarily give you an accurat e value. Use mysql_field_count if you want t o know t he t rue colum n count .

2.5.7 Python