Chapter 10. Perl
The
Perl
programming language has gone from a tool primarily used by Unix
systems administrators to the most widely used development platform
for the World Wide Web. Perl was not designed for the web, but its
ease of use and powerful text handling abilities have made it a
natural for CGI programming. Similarly, when mSQL first entered the
scene, its extremely small footprint and execution time were very
attractive to web developers who needed to serve thousands of
transactions a day. MySQL, with its enhanced speed and capabilities
provided an even greater incentive for web developers. Therefore, it
was only natural that a Perl interface to both MySQL and mSQL was
developed that allowed the best of both
worlds.
NOTE
At the time of this writing there are two interfaces between MySQL
and mSQL and Perl. The original consists of
Mysql.pm and Msql.pm,
custom interfaces that work only with MySQL and mSQL, respectively.
The other, newer, interface is a plug-in for the Database Independent
(DBI) set of modules. DBI is an attempt to provide a common Perl API
for all database accesses and enable greater behind-the-scenes
portability. The DBI interface has become the most robust and
standard, and the makers of MySQL recommend that all work be done
using DBI as development of the Mysql.pm and
Msql.pm modules has ceased. However, many legacy
systems still use these modules, so both will be covered here.
10.1. DBI
The recommended method for accessing MySQL and mSQL databases from
Perl is the DBD/DBI interface. DBD/DBI stands
for DataBase Dependent/DataBase Independent. The name arises from the
two-layer implementation of the interface. At the bottom is the
database dependent layer. Here, modules exist for each type of
database accessible from Perl. On top of these database dependent
modules lies a database independent layer. This is the interface that
you use to access the database. The advantage of this scheme is that
the programmer only has to learn one API, the database independent
layer. Every time a new database comes along, someone needs only to
write a DBD module for it and it will be accessible to all DBD/DBI
programmers.
As with all Perl modules, you must use DBI to get access:
#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);
use DBI;
NOTE
When running any MySQL/mSQL Perl programs, you should always include
the -w command line argument. With this present,
DBI will redirect all MySQL and mSQL specific error messages to
STDERR so that you can see any database errors
without checking for them explicitly in your program.
All interactions between Perl and MySQL and mSQL are conducted
through what is known as a database handle. The
database handle is an object -- represented
as a scalar
reference in Perl -- that implements all of the methods used to
communicate with the database. You may have as many database handles
open at once as you wish. You are limited only by your system
resources. The connect() method uses a connection format of
DBI:servertype:database:hostname:port (hostname
and port and optional), with additional arguments of username and
password to create a handle:
my $dbh = DBI->connect('DBI:mysql:mydata', undef, undef);
my $dbh = DBI->connect('DBI:mSQL:mydata:myserver', undef, undef);
my $dbh = DBI->connect('DBI:mysql:mydata','me','mypass');
The servertype
attribute is the name of the DBD
database-specific module, which in our case will be either
"mysql" or "mSQL" (note capitalization). The
first version creates a connection to the MySQL server on the local
machine via a Unix-style socket. This is the most efficient way to
communicate with the database and should be used if you are
connecting to a local server. If the hostname is supplied it will
connect to the server on that host using the standard port unless the
port is supplied as well. If you do not provide a username and
password when connecting to a MySQL server, the user executing the
program must have sufficient privileges within the MySQL database.
The username and password should always be left undefined for mSQL
databases.
NOTE
Perl 5 has two different calling conventions for modules. With the
object-oriented syntax, the arrow symbol
"->" is used to reference a method
in a particular class (as in DBI->connect).
Another method is the indirect syntax, in which the method name is
followed by the class name, then the arguments. The last
connect method above would be written as
connect DBI
'DBI:mysql:mydata',
'me',
'mypass'. Early versions of the
Msql.pm used the indirect syntax exclusively and
also enforced a specific method of capitalization inspired by the
mSQL C API. Therefore, a lot of older MsqlPerl
code will have lines in it like SelectDB $dbh
'test' where a simple
$dbh->selectdb('test')
would do. If you haven't guessed, we are partial to the
object-oriented syntax, if only because the arrow makes the
relationship between class and method clear.
Once you have connected to the MySQL or mSQL server, the database
handle -- $dbh in all of the examples in this
section -- is the gateway to the database server. For instance, to
prepare an SQL query:
$dbh->prepare($query);
WARNING
When using
mSQL you may select only one database
at a time for a particular database handle. The mSQL server imposes
this limit. However, you may change the current database at any time
by calling connect again. With
MySQL, you may include other databases
in your query by explicitly naming them. In addition, with both MySQL
and mSQL, if you need to access more than one database concurrently,
you can create multiple database handles and use them side by side.
Chapter 21, "Perl Reference", describes the full range of methods and
variables supplied by DBI as well as Mysql.pm
and Msql.pm.
As an example of the use of DBI consider the following simple
programs. In Example 10-1,
datashow.cgi accepts a hostname as a
parameter -- "localhost" is assumed if no parameter is
present. The program then displays all of the databases available on
that host.
Example 10-1. The CGI datashow.cgi Shows All of the Databases on a MySQL or mSQL Server
#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);
use CGI::Carp;
# Use the DBI module
use DBI;
CGI::use_named_parameters(1);
my ($server, $sock, $host);
my $output = new CGI;
$server = param('server') or $server = '';
# Prepare the MySQL DBD driver
my $driver = DBI->install_driver('mysql');
my @databases = $driver->func($server, '_ListDBs');
# If @databases is undefined we assume
# that means that the host does not have
# a running MySQL server. However, there could be other reasons
# for the failure. You can find a complete error message by
# checking $DBI::errmsg.
if (not @databases) {
print header, start_html('title'=>"Information on $server",
'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>$server</h1>
$server does not appear to have a running mSQL server.
</body></html>
END_OF_HTML
exit(0);
}
'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>$host</h1>
<p>
$host\'s connection is on socket $sock.
<p>
Databases:<br>
<UL>
END_OF_HTML
foreach (@databases) {
print "<LI>$_\n";
}
print <<END_OF_HTML;
</ul>
</body></html>
HTML
exit(0)
In Example 10-2, tableshow.cgi
accepts the name of a database server (default is
"localhost") and the name of a database on that server.
The program then shows all of the available tables on that server.
Example 10-2. The CGI tableshow.cgi Shows All of the Tables Within a Database
#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);
use CGI::Carp;
# Use the Msql.pm module
use DBI;
CGI::use_named_parameters(1);
my ($db);
my $output = new CGI;
$db = param('db') or die("Database not supplied!");
# Connect to the requested server.
my $dbh = DBI->connect("DBI:mysql:$db:$server", undef, undef);
# If $dbh does not exist, the attempt to connect to the
# database server failed. The server may not be running,
# or the given database may not exist.
if (not $dbh) {
print header, start_html('title'=>"Information on $host => $db",
'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>$host</h1>
<H2>$db</h2>
The connection attempt failed for the following reason:<BR>
$DBI::errstr
</body></html>
END_OF_HTML
exit(0);
}
print header, start_html('title'=>"Information on $host => $db",
'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>$host</h1>
<H2>$db</h2>
<p>
Tables:<br>
<UL>
END_OF_HTML
# $dbh->listtable returns an array of the tables that are available
# in the current database.
my @tables = $dbh->func( '_ListTables' );
foreach (@tables) {
print "<LI>$_\n";
}
print <<END_OF_HTML;
</ul>
</body></html>
END_OF_HTML
exit(0);
And, finally, Example 10-3 shows all of the
information about a specific table.
Example 10-3. The CGI tabledump.cgi Shows Information About a Specific Table
#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);
use CGI::Carp;
# Use the DBI module
use DBI;
CGI::use_named_parameters(1);
my ($db,$table);
my $output = new CGI;
$server = param('server') or $server = '';
$db = param('db') or die("Database not supplied!");
# Connect to the requested server.
my $dbh = DBI->connect("DBI:mysql:$db:$server", undef, undef);
# We now prepare a query for the server asking for all of the data in
# the table.
my $table_data = $dbh->prepare("select * from $table");
# Now send the query to the server.
$table_data->execute;
# If the return value is undefined, the table must not exist. (Or it could
# be empty; we don't check for that.)
if (not $table_data) {
print header, start_html('title'=>
"Information on $host => $db => $table", 'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>$host</h1>
<H2>$db</h2>
The table '$table' does not exist in $db on $host.
</body></html>
END_OF_HTML
exit(0);
}
# At this point, we know we have data to display. First we show the
# layout of the table.
print header, start_html('title'=>"Information on $host => $db => $table",
'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>$host</h1>
<H2>$db</h2>
<H3>$table</h3>
<p>
<TABLE BORDER>
<CAPTION>Fields</caption>
<TR>
<TH>Field<TH>Type<TH>Size<TH>NOT NULL
</tr>
<UL>
END_OF_HTML
# $table_data->name returns a referece to an array
# of the fields of the database.
my @fields = @{$table_data->NAME};
# $table_data->type return an array reference of the types of fields.
# The types returned here are in SQL standard notation, not MySQL specific.
my @types = @{$table_data->TYPE};
# $table_data->is_not_null returns a Boolean array ref indicating which fields
# have the 'NOT NULL' flag.
my @not_null = @{$table_data->is_not_null};
# $table_data->length return an array ref of the lengths of the fields. This is
# fixed for INT and REAL types, but variable (defined when the table was
# created) for CHAR.
my @length = @{$table_data->length};
# All of the above arrays were returned in the same order, so that $fields[0],
# $types[0], $not_null[0] and $length[0] all refer to the same field.
foreach $field (0..$#fields) {
print "<TR>\n";
print "<TD>$fields[$field]<TD>$types[$field]<TD>";
print $length[$field] if $types[$field] eq 'SQL_CHAR';
print "<TD>";
print 'Y' if ($not_null[$field]);
print "</tr>\n";
}
print <<END_OF_HTML;
</table>
<p>
<B>Data</b><br>
<OL>
END_OF_HTML
# Now we step through the data, row by row, using DBI::fetchrow_array().
# We save the data in an array that has the same order as the informational
# arrays (@fields, @types, etc.) we created earlier.
while(my(@data)=$table_data->fetchrow_array) {
print "<LI>\n<UL>";
for (0..$#data) {
print "<LI>$fields[$_] => $data[$_]</li>\n";
}
print "</ul></li>";
}
print <<END_OF_HTML;
</ol>
</body></html>
END_OF_HTML
 |  |  | | 9.5. CGI and Databases
|  | 10.2. An Example DBI Application |
Copyright © 2001 O'Reilly & Associates. All rights reserved.
|
|