I have two answers, one roughly sticks to your framework and the other changes the structure of the code but is how I would divide the problem.
The first version changes your execsql code to take a list of bind params too.
my $sql = 'select comp_name from company where comp_name = ?';
my $query = execsql($sql, $hash{comp_id});
sub execsql {
my ($sql, @bind_params) = @_;
my $sth = $dbh->prepare($sql) || error('prep_sql', $0);
$sth->execute(@bind_params) || error('sql', $0);
$dbh->commit;
return $sth;
}
The advantages are that it now handles escaping special SQL characters in your variables and putting the right delimiters around strings. The disadvantages are that you are doing more copying but you can get around that if you want by passing references.
What I would do if I were coding this from scratch and had free reign is to separate the DB access from the main application logic so that all SQL queries are hidden in modules (or at least subroutines) in case I ever needed to port to a different DB and the syntax needs to change. So:
my $dbh = get_db_handle();
my $company_name = get_company_name(company_id => $comp_id,
dbh => $dbh,
);
$dbh->disconnect;
sub get_db_handle {
# Do whatever you need to get the DB handle and set
# your favorite options
my $dbh = DBI->connect(...);
error('DB connect') unless defined $dbh;
}
sub get_company_name {
my %args = @_;
my $dbh = $args{dbh};
my $statement = 'select comp_name from company where comp_name = ?
+';
my $sth = $dbh->prepare($sql) || error('prep_sql', $0);
$sth->execute(@bind_params) || error('sql', $0);
my ($company_name) = $sth->fetchrow_array;
$sth->finish;
return $company_name;
}
You could certainly use the execsql code in each access subroutine. I prefer not to commit the DB handle inside the accessor functions so that I can call multiple accessors with the same handle and commit all of them as one transaction. In this case it does not matter since I am simply doing a select.
Hope that helped.
-ben |