Perl Program Repair Shop and Red Flags
Pages: 1, 2
Placeholders
The next obvious target is the VALUES section. There are two problems here. First, it's too long and there are too many repeated references to one variable; It would be better to abbreviate if we could. And second, if any of the @data items contains an apostrophe, the code will break. We can solve the second problem by using the DBI module's "placeholder" feature; this incidentally provides a simple solution to the first problem in the form of an array slice:
$sth = $dbh->prepare("SELECT * FROM info");
$sth->execute();
while (my @data = $sth->fetchrow_array) {
$SqlStatement4 = ("INSERT INTO Customer_Information " .
"(CustomerNumber, Name, Address1, Address2, City, State,
ZipCode, Email, DefaultPassword) " .
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ");
my $sth4 = $dbh->prepare($SqlStatement4);
$sth4->execute(@data[1..4, 6..9, 16]);
}
Loop hoisting
There's no need to prepare the statement every time through the loop, because it never changes. (This is another benefit of the placeholder approach.) So we can get a performance win by hoisting the prepare out of the loop;
$sth = $dbh->prepare("SELECT * FROM info");
$sth->execute();
$SqlStatement4 = ("INSERT INTO Customer_Information " .
"(CustomerNumber, Name, Address1, Address2, City, State,
ZipCode, Email, DefaultPassword) " .
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ");
my $sth4 = $dbh->prepare($SqlStatement4);
while (my @data = $sth->fetchrow_array) {
$sth4->execute(@data[1..4, 6..9, 16]);
}
This has the pleasant side-effect of turning the main loop of the routine from something long and complicated into something compact and simple.
The dot operator
Now let's clean up the SQL statement itself. Here we see another red flag: The dot operator. Yes, the dot operator is a red flag! It's rarely necessary, and usually a mistake. Here we can do better by writing the SQL like this:
$sth = $dbh->prepare("SELECT * FROM info");
$sth->execute();
$SqlStatement4 =
"INSERT INTO Customer_Information
(CustomerNumber, Name, Address1, Address2,
City, State, ZipCode, Email, DefaultPassword)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
";
my $sth4 = $dbh->prepare($SqlStatement4);
while (my @data = $sth->fetchrow_array) {
$sth4->execute(@data[1..4, 6..9, 16]);
}
We've just eliminated eight punctuation characters that were cluttering up the SQL. Now another programmer looking at the string can see it instantly without having to filter out a bunch of superfluous periods and quotation marks. (Note that I also got rid of two superstitious parentheses. Perl has too much punctuation, so it's good to take advantage of opportunities to reduce it.)
Omit needless variables
Finally, the variable $SqlStatement4 is useless. It's only used once, and its name has no documentary value, so we might as well get rid of it:
$sth = $dbh->prepare("SELECT * FROM info");
$sth->execute();
my $sth4 = $dbh->prepare(qq{
INSERT INTO Customer_Information
(CustomerNumber, Name, Address1, Address2,
City, State, ZipCode, Email, DefaultPassword)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
});
while (my @data = $sth->fetchrow_array) {
$sth4->execute(@data[1..4, 6..9, 16]);
}
There are more improvements to be made here. For example, now that the code is cleaned up, it's easy to see that it can simply be replaced with a single INSERT statement. But this is enough for now.
The original code was 815 characters long, and the result is 451 characters, a 45 percent reduction. Programmers have learned to distrust length measurements like this -- so much so that they often automatically reply "But you can reduce the length of any program by squeezing out the whitespace." But here we've made the program shorter while increasing the amount of whitespace. This is not unusual.
For more information
If you found this interesting, you will probably like the class. You may also enjoy the following www.perl.com articles, which represent some of my earlier thinking on this topic.
- Program Repair Shop and Red Flags I (May 2000)
- Program Repair Shop and Red Flags II (June 2000)
- Program Repair Shop and Red Flags III (November 2000)
You may also want to peek at http://www.plover.com/~mjd/misc/flags/ which has a few samples of the slides that I am working on for the class.
Mark-Jason Dominus has been programming in Perl since 1993 and is the former managing editor of www.perl.com.
Return to the O'Reilly Network.
Showing messages 1 through 4 of 4.
-
2001-06-19 05:24:25 asgarr [View]
-
Red Flag in the SQL !
2001-06-08 04:39:34 mikewhit [View]
The line "SELECT * FROM info" is also asking for trouble as soon as the database format changes.
You should always explicitly state the fields you want to retrieve in a SELECT statement, if your app relies on fields being in a particular order - which this one does.
-
Turning the Tides on Perl's Attitude Toward Beginners
2001-06-06 12:08:11 cedmond [View]
The subject of this post is the title of another article listed on the home page of www.perl.com. The idea expressed in the title is something that the author of this article should think about. I'm sure he is very proud of himself for pointing out the "wretchedness" of some beginner's code, however during the process of learning anything it is inevitable that one will create something less than perfect before learning the most efficient way. The role of the experienced programmer should be to encourage those that are learning, to guide them through their mistakes without degrading them and show them why there are better ways to do it. After all isn't the perl motto "There is more than one way to do it"? If one of those ways happens to be the beginner's way, then so be it.
-
return undef
2001-05-18 14:36:42 mjd [View]
This is a nice one that I forgot to put in the class.
People like to write return undef
to make a function that returns a false value,
but this is often wrong, because in list context,
return undef returns true.
(It returns a list with a single undefined element.
But only an empty list is false.)
Some programmers are aware of this problem, and write
return wantarray ? () : undef; which works,
but is unnecessarily complicated.
The best answer here is to simply write return;
which returns an undefined scalar value in scalar context,
and an empty list in list context.










