mod_perl2 newbie DBI question

mod_perl2 newbie DBI question

am 12.06.2008 20:09:00 von Brian Gaber

This is a multi-part message in MIME format.

------_=_NextPart_001_01C8CCB7.63C07F87
Content-Type: text/plain;
charset="US-ASCII"
Content-Transfer-Encoding: quoted-printable

I have a MySQL database application that is used and managed by cgi-bin
scripts (CGI.pm). In development the performance was fine, but a
productin trial showed the performance to be unacceptable. I am
attempting to fix the performance by using mod_perl2 which I have never
used. I have successfully compiled and install mod_perl2 and have added
these entries into httpd.conf:

PerlModule Apache::DBI

PerlModule ModPerl::Registry
Alias /perl/ /usr/local/apache2/perl/

SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions +ParseHeaders
Options +ExecCGI
Order allow,deny
Allow from all


PerlModule ModPerl::PerlRun
Alias /perl-run/ /usr/local/apache2/perl-run/

SetHandler perl-script
PerlResponseHandler ModPerl::PerlRun
PerlOptions +ParseHeaders +GlobalRequest
Options +ExecCGI
Order allow,deny
Allow from all


I have modified scripts to work in mod_perl2, but they don't
work reliably. Sometimes they work and then they stop working and then
I have to stop and start Apache to get it working again. I am hoping if
I supply one of my scripts someone can advise me what needs to be done
to make in work fast and reliably and then I can use this as an example
to fix my other scripts. Here is the one script:

#!/usr/bin/perl -w

#use CGI qw/:standard :html3 :netscape/;
use CGI '-autoload';
use DBI();
use warnings;
use strict;

my $region =3D param('region');

my $JSCRIPT=3D< function changeTitle()
{
parent.document.title=3Ddocument.title;
}

function validate(theForm)
{
theForm.submit();
}
JSEND

my $LOCAL_STYLE=3D<
body {
font-family:Verdana;
font-size:12px;
}

.btn {
font-family:Verdana;
font-size:9px;
color:black;
border:1px solid #000000;
margin-top:5px;
background-color:white
}

table {
font-family:Verdana;
border:1px solid #000000;
background-color:white
}

th {
font-family:Verdana;
font-size:12px;
color:black;
}

td.right {
font-family:Verdana;
font-size:12px;
color:black;
text-align:right;
}

td {
font-family:Verdana;
font-size:12px;
color:black;
text-align:center;
}

CSSEND

print header( -type =3D> "text/html" );
print start_html( -title =3D> "Title", =
-style=3D>{-code=3D>$LOCAL_STYLE},
-onLoad=3D>"changeTitle()", -script=3D>$JSCRIPT ),
br({ -clear =3D> 'all' }),
"\n";

# Connect to the database.
my $dbh =3D DBI->connect("DBI:mysql:database=3Desnap;host=3Dlocalhost",
"athena", "godess",
{'RaiseError' =3D> 1});

# Determine MySQL locks table name
my $sth =3D $dbh->prepare("SELECT * FROM region_props WHERE region =3D
'$region'");
$sth->execute();
my $ref =3D $sth->fetchrow_hashref();
$sth->finish();
my $locks_table =3D $ref->{locks_table};

my @form_vars =3D param();

if ( @form_vars > 1 ) { # if required parameters were passed
rm_lock();
}

print_form(); # Display the MySQL table

# Disconnect from the database.
$dbh->disconnect();

print end_html();

sub print_form {
my $i =3D 0;
my @clmnNames =3D ();
my @rows =3D ();

my $sth =3D $dbh->prepare("SELECT * FROM $locks_table");
$sth->execute();

while (my $ref =3D $sth->fetchrow_hashref()) {
push(@rows, td({-class=3D>'centre'},checkbox(-name=3D>"ckbx_$i",
-value=3D>"$ref->{id}", -label=3D>'')).
td({-class=3D>'centre'},$ref->{id}).
td({-class=3D>'centre'},$ref->{rcd_opener}).
td({-class=3D>'centre'},$ref->{lock_date})
);
$i++;
}
$sth->finish();

# Specified values for table column heading names
$clmnNames[0] =3D "Select";
$clmnNames[1] =3D "Id";
$clmnNames[2] =3D "User Id";
$clmnNames[3] =3D "Date";

print start_form(),
font({-face=3D>"Trebuchet MS, Arial", -size=3D>2},br(),
center(strong("Delete Record Locks"),br(),br(),
table({-class=3D>'bdr', -width=3D>'100%', -BgColor=3D>"white",
-border=3D>'0'},
Tr([th(\@clmnNames)]),"\n",
Tr([@rows])),"\n",
button(-class=3D>"btn", -value=3D>"Delete selected record locks",
-onClick=3D>"validate(this.form)"),
));
print hidden(-name=3D>'region', -value=3D>param('region')),"\n";
print end_form();
}

sub rm_lock() {

foreach ( param() ) {
if ($_ =3D~ /^ckbx_\d+$/) {
my $id2del =3D param($_);

# Delete row from $locks_table
$dbh->do("DELETE FROM $locks_table WHERE id=3D'$id2del'");

my $errno =3D $dbh->{mysql_errno};
my $errTxt =3D $dbh->{mysql_error};
if ( $errno > 0 ) {
print center(font({-face=3D>"Trebuchet MS, Arial", =
-size=3D>2,
-color=3D>"red"}),b("Error deleting row from $locks_table,
",font({-color=3D>"black"},"MySQL Error Code: $errno -
$errTxt"))),"\n",p();
}
}
}
}

------_=_NextPart_001_01C8CCB7.63C07F87
Content-Type: text/html;
charset="US-ASCII"
Content-Transfer-Encoding: quoted-printable




charset=3Dus-ascii">
6.5.7652.24">
mod_perl2 newbie DBI question




I have a MySQL database application =
that is used and managed by cgi-bin scripts (CGI.pm).  In =
development the performance was fine, but a productin trial showed the =
performance to be unacceptable.  I am attempting to fix the =
performance by using mod_perl2 which I have never used.  I have =
successfully compiled and install mod_perl2 and have added these entries =
into httpd.conf:



PerlModule Apache::DBI



PerlModule ModPerl::Registry


Alias /perl/ =
/usr/local/apache2/perl/



<Location /perl/>


    SetHandler =
perl-script



    PerlResponseHandler =
ModPerl::Registry



    PerlOptions =
+ParseHeaders



    Options =
+ExecCGI



    Order =
allow,deny



    Allow from =
all



</Location>



PerlModule ModPerl::PerlRun


Alias /perl-run/ =
/usr/local/apache2/perl-run/



<Location /perl-run/>


    SetHandler =
perl-script



    PerlResponseHandler =
ModPerl::PerlRun



    PerlOptions =
+ParseHeaders +GlobalRequest



    Options =
+ExecCGI



    Order =
allow,deny



    Allow from =
all



</Location>



        FACE=3D"Arial">I have modified scripts to work in mod_perl2, but they =
don't work reliably.  Sometimes they work and then they stop =
working and then I have to stop and start Apache to get it working =
again.  I am hoping if I supply one of my scripts someone can =
advise me what needs to be done to make in work fast and reliably and =
then I can use this as an example to fix my other scripts.  Here is =
the one script:



#!/usr/bin/perl -w



#use CGI qw/:standard :html3 =
:netscape/;



use CGI '-autoload';


use DBI();


use warnings;


use strict;



my $region =3D param('region');



my $JSCRIPT=3D<<JSEND;


   function =
changeTitle()



   {


      =
parent.document.title=3Ddocument.title;



   }



   function =
validate(theForm)



   {


      =
theForm.submit();



   }


JSEND



my =
$LOCAL_STYLE=3D<<CSSEND;



   body {


      =
font-family:Verdana;



      =
font-size:12px;



   }



   .btn {


      =
font-family:Verdana;



      =
font-size:9px;



      =
color:black;



      =
border:1px solid #000000;



      =
margin-top:5px;



      =
background-color:white



   }



   table {


      =
font-family:Verdana;



      =
border:1px solid #000000;



      =
background-color:white



   }



   th {


      =
font-family:Verdana;



      =
font-size:12px;



      =
color:black;



   }



   td.right {


      =
font-family:Verdana;



      =
font-size:12px;



      =
color:black;



      =
text-align:right;



   }



   td {


      =
font-family:Verdana;



      =
font-size:12px;



      =
color:black;



      =
text-align:center;



   }



CSSEND



print header( -type =3D> =
"text/html" );



print start_html( -title =3D> =
"Title", -style=3D>{-code=3D>$LOCAL_STYLE}, =
-onLoad=3D>"changeTitle()", -script=3D>$JSCRIPT =
),



      br({ =
-clear =3D> 'all' }),



      =
"\n";



# Connect to the database.


my $dbh =3D =
DBI->connect("DBI:mysql:database=3Desnap;host=3Dlocalhost",<=
/FONT>


FACE=3D"Arial">         &nbs=
p;            =
"athena", "godess",



FACE=3D"Arial">         &nbs=
p;            =
{'RaiseError' =3D> 1});



# Determine MySQL locks table =
name



my $sth =3D =
$dbh->prepare("SELECT * FROM region_props WHERE region =3D =
'$region'");



$sth->execute();


my $ref =3D =
$sth->fetchrow_hashref();



$sth->finish();


my $locks_table =3D =
$ref->{locks_table};



my @form_vars =3D param();



if ( @form_vars > 1 ) { # if =
required parameters were passed



   rm_lock();


}



FACE=3D"Arial">print_form();       &nb=
sp;   # Display the MySQL table



# Disconnect from the database.


$dbh->disconnect();



print end_html();



sub print_form {


   my $i =3D 0;


   my @clmnNames =3D =
();



   my @rows =3D ();



   my $sth =3D =
$dbh->prepare("SELECT * FROM $locks_table");



   =
$sth->execute();



   while (my $ref =3D =
$sth->fetchrow_hashref()) {



      =
push(@rows, =
td({-class=3D>'centre'},checkbox(-name=3D>"ckbx_$i", =
-value=3D>"$ref->{id}", -label=3D>'')).



FACE=3D"Arial">         &nbs=
p;        =
td({-class=3D>'centre'},$ref->{id}).



FACE=3D"Arial">         &nbs=
p;        =
td({-class=3D>'centre'},$ref->{rcd_opener}).



FACE=3D"Arial">         &nbs=
p;        =
td({-class=3D>'centre'},$ref->{lock_date})



      =
);



      =
$i++;



   }


   $sth->finish();



   # Specified values for =
table column heading names



   $clmnNames[0] =3D =
"Select";



   $clmnNames[1] =3D =
"Id";



   $clmnNames[2] =3D =
"User Id";



   $clmnNames[3] =3D =
"Date";



   print start_form(),


      =
font({-face=3D>"Trebuchet MS, Arial", =
-size=3D>2},br(),



      =
center(strong("Delete Record Locks"),br(),br(),



      =
table({-class=3D>'bdr', -width=3D>'100%', =
-BgColor=3D>"white", -border=3D>'0'},



      =
Tr([th(\@clmnNames)]),"\n",



      =
Tr([@rows])),"\n",



      =
button(-class=3D>"btn", -value=3D>"Delete selected =
record locks", =
-onClick=3D>"validate(this.form)"),



      =
));



      print =
hidden(-name=3D>'region', =
-value=3D>param('region')),"\n";



   print end_form();


}



sub rm_lock() {



   foreach ( param() ) =
{



      if ($_ =
=3D~ /^ckbx_\d+$/) {



FACE=3D"Arial">         my =
$id2del =3D param($_);



FACE=3D"Arial">         # Delete =
row from $locks_table



FACE=3D"Arial">         =
$dbh->do("DELETE FROM $locks_table WHERE =
id=3D'$id2del'");



FACE=3D"Arial">         my =
$errno =3D $dbh->{mysql_errno};



FACE=3D"Arial">         my =
$errTxt =3D $dbh->{mysql_error};



FACE=3D"Arial">         if ( =
$errno > 0 ) {



FACE=3D"Arial">         &nbs=
p;  print center(font({-face=3D>"Trebuchet MS, Arial", =
-size=3D>2, -color=3D>"red"}),b("Error deleting row =
from $locks_table, =
",font({-color=3D>"black"},"MySQL Error Code: =
$errno - $errTxt"))),"\n",p();



FACE=3D"Arial">         }


      =
}



   }


}





------_=_NextPart_001_01C8CCB7.63C07F87--

Re: mod_perl2 newbie DBI question

am 12.06.2008 20:23:53 von mpeters

Brian Gaber wrote:

> I have modified scripts to work in mod_perl2, but they don't
> work reliably. Sometimes they work and then they stop working and then
> I have to stop and start Apache to get it working again.

"stop working" is really too generic a description for anyone to really
diagnose. It's kind of like telling your doctor "I'm sick, here's my picture,
tell me what's wrong" :)

> I am hoping if
> I supply one of my scripts someone can advise me what needs to be done
> to make in work fast and reliably and then I can use this as an example
> to fix my other scripts. Here is the one script:

The best way to get help is to make the problem script as small as possible. As
it stands right now, it's a bit like homework trying to read through your problem.

--
Michael Peters
Plus Three, LP

Re: mod_perl2 newbie DBI question

am 12.06.2008 20:36:20 von Jim Brandt

Michael Peters wrote:
> Brian Gaber wrote:
>
>> I have modified scripts to work in mod_perl2, but they don't
>> work reliably. Sometimes they work and then they stop working and then
>> I have to stop and start Apache to get it working again.
>
> "stop working" is really too generic a description for anyone to really
> diagnose. It's kind of like telling your doctor "I'm sick, here's my picture,
> tell me what's wrong" :)

Some information that can help:

Is there anything useful in the Apache error log?

What happens in the browser when it stops working?

What platform are you running on (windows, linux, etc.)?

Are you seeing this in your development environment?

Can you reproduce the problem reliably?


>
>> I am hoping if
>> I supply one of my scripts someone can advise me what needs to be done
>> to make in work fast and reliably and then I can use this as an example
>> to fix my other scripts. Here is the one script:
>
> The best way to get help is to make the problem script as small as possible. As
> it stands right now, it's a bit like homework trying to read through your problem.
>

--
Jim Brandt
Administrative Computing Services
University at Buffalo

RE: mod_perl2 newbie DBI question

am 12.06.2008 20:54:30 von Brian Gaber

Answers:

Is there anything useful in the Apache error log? Works fine a few
times and then
DBD::mysql::st execute failed: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the
right syntax to use near '' at line 1 at
/usr/local/apache2/perl-run/regDelLocks.pl line 191.\n

What happens in the browser when it stops working?
The server encountered an internal error or misconfiguration and was
unable to complete your request.

What platform are you running on (windows, linux, etc.)?
Server is AIX. Client is Windows.

Are you seeing this in your development environment? Yes

Can you reproduce the problem reliably? Yes

Thanks.

-----Original Message-----
From: Jim Brandt [mailto:cbrandt@buffalo.edu]=20
Sent: Thursday, June 12, 2008 2:36 PM
To: Michael Peters
Cc: Brian Gaber; modperl@perl.apache.org
Subject: Re: mod_perl2 newbie DBI question



Michael Peters wrote:
> Brian Gaber wrote:
>=20
>> I have modified scripts to work in mod_perl2, but they don't=20
>> work reliably. Sometimes they work and then they stop working and=20
>> then I have to stop and start Apache to get it working again.
>=20
> "stop working" is really too generic a description for anyone to=20
> really diagnose. It's kind of like telling your doctor "I'm sick,=20
> here's my picture, tell me what's wrong" :)

Some information that can help:

Is there anything useful in the Apache error log?

What happens in the browser when it stops working?

What platform are you running on (windows, linux, etc.)?

Are you seeing this in your development environment?

Can you reproduce the problem reliably?


>=20
>> I am hoping if
>> I supply one of my scripts someone can advise me what needs to be=20
>> done to make in work fast and reliably and then I can use this as an=20
>> example to fix my other scripts. Here is the one script:
>=20
> The best way to get help is to make the problem script as small as=20
> possible. As it stands right now, it's a bit like homework trying to
read through your problem.
>=20

--
Jim Brandt
Administrative Computing Services
University at Buffalo

Re: mod_perl2 newbie DBI question

am 12.06.2008 21:04:55 von mpeters

Brian Gaber wrote:

> Is there anything useful in the Apache error log? Works fine a few
> times and then
> DBD::mysql::st execute failed: You have an error in your SQL syntax;
> check the manual that corresponds to your MySQL server version for the
> right syntax to use near '' at line 1 at
> /usr/local/apache2/perl-run/regDelLocks.pl line 191.\n

What does the SQL statement you are executing look like when it encounters this
problem? It's possible that you aren't getting what you think you should in
param('region').

quoting your earlier email:
> my $dbh = DBI->connect("DBI:mysql:database=esnap;host=localhost",
> "athena", "godess",
> {'RaiseError' => 1});
>
> # Determine MySQL locks table name
> my $sth = $dbh->prepare("SELECT * FROM region_props WHERE region =
> '$region'");
> $sth->execute();

Btw, this is *really* bad security wise. $region is coming straight from the
browser. You're setting yourself up for an SQL Injection attack. Imagine I
request some URL like:

regDelLocks.pl?region= %27blah%27%3B+DROP+ALL+DATABASES

Guess what will happen? Preventing this is really easy. Just use SQL bind params.

my $sth = $dbh->prepare("SELECT * FROM region_props WHERE region = ?");
$sth->execute($region);

--
Michael Peters
Plus Three, LP

Re: mod_perl2 newbie DBI question

am 12.06.2008 22:30:59 von el.dodgero

2008/6/12 Michael Peters :
> Brian Gaber wrote:

>> # Determine MySQL locks table name
>> my $sth = $dbh->prepare("SELECT * FROM region_props WHERE region =
>> '$region'");
>> $sth->execute();

> Btw, this is *really* bad security wise. $region is coming straight from the
> browser. You're setting yourself up for an SQL Injection attack. Imagine I
> request some URL like:

> regDelLocks.pl?region= %27blah%27%3B+DROP+ALL+DATABASES

> Guess what will happen? Preventing this is really easy. Just use SQL bind params.

> my $sth = $dbh->prepare("SELECT * FROM region_props WHERE region = ?");
> $sth->execute($region);

Hear hear!

It can be true that sometimes you can't (or have good reason to not
want to) use placeholders.

For instance,it's feasible to build a totally dynamic query by pushing
whereclauses into an array and values into another array.

It's also feasible to build a variable length IN() list against some
array like, for instance, "WHERE colname IN(@{[(join ', ', '?' x
scalar @yourarray]}" into the statement (or using a sprintf much the
same way), it often doesn't read well.

In those cases it's understandable that you'd want to build your query
dynamically, but there's no excuse for unsafeness!

Examples:

Placeholders:
my @things_to_check_for = qw(foo bar baz luhrman);
my $q = <<"EOF";
SELECT thingy
FROM doohickeys
WHERE type IN(@{[join ', ', ('?') x scalar @things_to_check_for]})
EOF
my $st = $dbh->prepare($q);
$st->execute(@things_to_check_for);

# if the @{[]} boggles you
my @things_to_check_for = qw(foo bar baz luhrman);
my $q = sprintf <<"EOF", join ', ', ('?') x scalar @things_to_check_for;
SELECT thingy
FROM doohickeys
WHERE type IN(%s)
EOF
my $st = $dbh->prepare($q);
$st->execute(@things_to_check_for);

# More explictly:
my @things_to_check_for = qw(foo bar baz luhrman);
my $things_to_check_for = join ', ', ('?') x scalar @things_to_check_for;
my $q = sprintf <<"EOF";
SELECT thingy
FROM doohickeys
WHERE type IN($things_to_check_for)
EOF
my $st = $dbh->prepare($q);
$st->execute(@things_to_check_for);

This is a rather contrived example using a very simple query however.
Getting more complex than that, or if you're pandering to PHP
programmers (who are used to their crap database interfaces that only
let you use bind variables on input if you intend to use them on
output too, for no apparent reason), you have potential reasons to
directly create your statement (also, it makes it easier to spit out a
runnable query for debugging, because you can just print the very
statement out*).

So use DBI's quote() method:

Safe without placeholders:

my @things_to_check_for = qw(foo bar baz luhrman);
my $q = <<"EOF";
SELECT thingy
FROM doohickeys
WHERE type IN(@{[join ', ', map $dbh->quote($_), @things_to_check_for]})
EOF

(if you are using placeholders everywhere, quote can still be useful
for debugging, assuming you are ONLY using placeholders or at the very
least not writing any statements with a literal question mark in
them). You can take your statement with placeholders and do this:

my $rows = $st->execute($value1, $value2, $value3);

unless ($rows) {
my $show_query = $q;
$show_query =~ s/\?/\%s/g;
printf <<"EOF", $st->errstr, map $dbh->quote($_), $value1, $value2, $value3;


SQL Statement failed: %s

$show_query


EOF
}

--
Dodger

Re: mod_perl2 newbie DBI question

am 13.06.2008 04:07:20 von David Kaufman

Hi Brian,

"Brian Gaber" wrote...


my $region = param('region'); # ...

my $sth = $dbh->prepare(
"SELECT * FROM region_props WHERE region = '$region'"
);


Works fine a few times and then:

DBD::mysql::st execute failed: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the
right syntax to use near '' at line 1 at
/usr/local/apache2/perl-run/regDelLocks.pl line 191.\n


As Michael pointed out, the problem is caused by inserting uncheched user
input into your SQL query. If the CGI paramter $region, which you've
surrounded in single-quotes happens to *contain* a single-quote... boom! A
SQL syntax error exactly as you report results, becuase MySQL sees this:

SELECT * FROM region_props WHERE region = 'Joe's Region'

See the problem? Newlines, carriage returns and other control characters
can sometimes surprise you too (not to mention the intentional
sql-injection attacks michael points out). By using SQL placeholders, DBI
will helpfully escape any and all problematic characters in $region,
ensuring that it is at least safe (even if not entirely valid or correct
correct) before passing it to the db, so the MySQL server gets:

SELECT * FROM region_props WHERE region = 'Joe\'s Region'

hth,

-dave

RE: mod_perl2 newbie DBI question

am 13.06.2008 14:25:40 von Brian Gaber

Michael,

Thank you very much for this valuable advice.

Cheers.

Brian=20

-----Original Message-----
From: Michael Peters [mailto:mpeters@plusthree.com]=20
Sent: Thursday, June 12, 2008 3:05 PM
To: Brian Gaber
Cc: Jim Brandt; modperl@perl.apache.org
Subject: Re: mod_perl2 newbie DBI question

Brian Gaber wrote:

> Is there anything useful in the Apache error log? Works fine a few=20
> times and then DBD::mysql::st execute failed: You have an error in=20
> your SQL syntax; check the manual that corresponds to your MySQL=20
> server version for the right syntax to use near '' at line 1 at=20
> /usr/local/apache2/perl-run/regDelLocks.pl line 191.\n

What does the SQL statement you are executing look like when it
encounters this problem? It's possible that you aren't getting what you
think you should in param('region').

quoting your earlier email:
> my $dbh =3D =
DBI->connect("DBI:mysql:database=3Desnap;host=3Dlocalhost",
> "athena", "godess",
> {'RaiseError' =3D> 1});
>
> # Determine MySQL locks table name
> my $sth =3D $dbh->prepare("SELECT * FROM region_props WHERE region =3D =

> '$region'"); $sth->execute();

Btw, this is *really* bad security wise. $region is coming straight from
the browser. You're setting yourself up for an SQL Injection attack.
Imagine I request some URL like:

regDelLocks.pl?region=3D %27blah%27%3B+DROP+ALL+DATABASES

Guess what will happen? Preventing this is really easy. Just use SQL
bind params.

my $sth =3D $dbh->prepare("SELECT * FROM region_props WHERE region =3D =
?");
$sth->execute($region);

--
Michael Peters
Plus Three, LP