Created
October 28, 2014 10:17
-
-
Save passos/d4ca20af7a6dd2d8f850 to your computer and use it in GitHub Desktop.
DBUtils for Perl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package DBUtils; | |
require Exporter; | |
@ISA = qw(Exporter); | |
@EXPORT = @EXPORT_OK = qw( | |
prepare_stat | |
exec_sql | |
exec_stat | |
query_data | |
query_stat | |
query_one | |
query_value | |
query_json_data | |
db_save_data | |
); | |
use strict; | |
use lib "..", "../.."; | |
use Defs; | |
use DBI; | |
use Devel::StackTrace; | |
use Singleton; | |
use Data::Dumper; | |
use Log; | |
use Log::Log4perl; | |
Log::Log4perl->wrapper_register(__PACKAGE__); | |
sub clean_sql { | |
my ( $sql ) = @_; | |
(WARN "empty SQL: $sql" and return '') if (not $sql or $sql =~ /^\s*$/ ); | |
$sql =~ s/^\s+//; | |
$sql =~ s/\s+$//; | |
$sql =~ s/\t/ /g; | |
return "\n\n$sql\n"; | |
} | |
sub prepare_stat { | |
shift if (@_ > 0 and ref($_[0])); | |
my ( $sql ) = @_; | |
DEBUG '[', caller, "] prepare SQL: $sql"; | |
return get_dbh()->prepare($sql); | |
} | |
# sample: exec_sql(qq[INSERT/UPDATE...], 1, 2, 3) | |
# return: dbi statement | |
sub exec_sql { | |
shift if (@_ > 0 and ref($_[0])); | |
my ( $sql, @data ) = @_; | |
my @sql_list = split(';', $sql); | |
my $sth; | |
for my $sql (@sql_list) { | |
$sql ||= ''; | |
$sql = clean_sql($sql); | |
next if $sql eq ''; | |
DEBUG '[', caller, "] SQL $sql \nwith data: ", list_to_str(\@data); | |
$sth = get_dbh()->prepare($sql); | |
my $result = $sth->execute(@data); | |
if ($result) { | |
DEBUG "last inserted id: ", $sth->{mysql_insertid} if $sql =~ /insert /i; | |
} | |
DEBUG "-"x60; | |
} | |
return $sth; | |
} | |
# sample: $sth=prepare_stat(SQL); exec_stat($sth, 1, 2, 3) | |
# return: dbi statement | |
sub exec_stat { | |
my ( $sth, @data ) = @_; | |
DEBUG '[', caller, "] SQL with data: ", list_to_str(\@data); | |
my $result = $sth->execute(@data); | |
if ($result) { | |
DEBUG "last inserted id: ", $sth->{mysql_insertid}; | |
} | |
else { | |
ERROR "execute statement error: ", $sth->errstr; | |
} | |
DEBUG "-"x60; | |
return $sth; | |
} | |
# sample: $sth=prepare_stat(SQL); $rows=$query_stat($sth, 1, 2, 3); | |
# return: multiple rows of query result, list reference of hashrefs | |
sub query_stat { | |
shift if (@_ > 0 and ref($_[0])); | |
my ( $sql, @data ) = @_; | |
$sql = clean_sql($sql); | |
return undef if $sql eq ''; | |
DEBUG '[', caller, "] SQL $sql \nwith data: ", list_to_str(\@data); | |
my $sth = get_dbh()->prepare($sql); | |
$sth->execute(@data) or ERROR "execute SQL: $sql, error: ", $sth->errstr; | |
DEBUG "last inserted id: ", $sth->{mysql_insertid} if ( $sql =~ /INSERT/i ); | |
DEBUG "result count: ", $sth->rows; | |
DEBUG "-"x60; | |
return $sth; | |
} | |
# sample: $rows = query_data(SQL, 1, 2, 3); | |
# return: multiple rows of query result, list reference of hashrefs | |
sub query_data { | |
shift if (@_ > 0 and ref($_[0])); | |
my ( $sql, @data ) = @_; | |
$sql = clean_sql($sql); | |
return [] if $sql eq ''; | |
DEBUG '[', caller, "] SQL $sql \nwith data: ", list_to_str(\@data); | |
my $sth = get_dbh()->prepare($sql); | |
$sth->execute(@data) or ERROR "execute SQL: $sql, error: ", $sth->errstr; | |
my @result = (); | |
while ( my $row = $sth->fetchrow_hashref() ) { | |
push @result, $row; | |
} | |
DEBUG "result count: ", scalar @result; | |
DEBUG "-"x60; | |
return \@result; | |
} | |
# sample: $row = query_one(SQL, 1, 2, 3); | |
# return: first row of query result, a hashref | |
sub query_one { | |
shift if (@_ > 0 and ref($_[0])); | |
my ( $sql, @data ) = @_; | |
$sql = clean_sql($sql); | |
return {} if $sql eq ''; | |
DEBUG '[', caller, "] SQL $sql \nwith data: ", list_to_str(\@data); | |
my $sth = get_dbh()->prepare($sql); | |
$sth->execute(@data) or ERROR "execute SQL: $sql, error: ", $sth->errstr; | |
my $result = $sth->fetchrow_hashref(); | |
#DEBUG "result: ", Dumper($result); | |
DEBUG "-"x60; | |
return $result; | |
} | |
# sample: $row = query_value(SQL, 1, 2, 3); | |
# return: first field of first row of query result, a scalar value | |
sub query_value { | |
shift if (@_ > 0 and ref($_[0])); | |
my ( $sql, @data ) = @_; | |
$sql = clean_sql($sql); | |
return undef if $sql eq ''; | |
DEBUG '[', caller, "] SQL $sql \nwith data: ", list_to_str(\@data); | |
my $sth = get_dbh()->prepare($sql); | |
$sth->execute(@data) or ERROR "execute SQL: $sql, error: ", $sth->errstr; | |
my @ary = $sth->fetchrow_array; | |
my $result = (scalar @ary > 0) ? $ary[0] : undef; | |
DEBUG "Result: $result "."-"x40; | |
return $result; | |
} | |
# sample: $json = query_json_data(SQL, 1, 2, 3) | |
# return: json format of query_data result | |
sub query_json_data { | |
shift if (@_ > 0 and ref($_[0])); | |
my ( $sql, @data ) = @_; | |
my $result = query_data( $sql, @data ); | |
return JSON::to_json( $result ); | |
} | |
sub print_stack_trace { | |
my $trace = Devel::StackTrace->new; | |
return $trace->as_string; # like carp | |
} | |
# a generic method to save a hashref record to database | |
# and return key field | |
# | |
# input: a data row in hashref, save it to db | |
# if key_field has value, then use update, otherwise use insert | |
# | |
# options: the key_field must be an auto-increasement field | |
# | |
# sample; | |
# # insert a new data | |
# my $row = {}; | |
# $row->{'strName'} = 'abc'; | |
# $row->{'strValue'} = 123; | |
# my $id = db_save_data('a_table', $row, id=>{'intID'}); | |
# $row = query_one(SQL, $id); | |
# | |
# # clone this row | |
# $row->{"intID"} = undef; | |
# db_save_data("a_table", $row, {id=>"intID"}); | |
# | |
# # update this row | |
# $row->{"some_field"} = 123; | |
# db_save_data("a_table", $row, {id=>"intID"}); | |
# | |
sub db_save_data { | |
my ($tablename, $data, $extra) = @_; | |
my $key_field = $extra->{'key'} || ''; | |
my @fields = keys %$data; | |
# filter out the key field from field list | |
if ($key_field) { | |
@fields = grep { $_ ne $key_field } @fields; | |
} | |
# bind params for SQL | |
my @values = @$data{@fields}; | |
if ($key_field and $data->{$key_field}) { | |
# update data | |
my $field_placeholders = join(', ', map {"$_ = ?"} @fields); | |
my $SQL = qq[ | |
UPDATE $tablename | |
SET $field_placeholders | |
WHERE $key_field = ? | |
]; | |
push @values, $data->{$key_field}; | |
DEBUG '[', caller, "] saving data", Dumper($data), "\nby $SQL \nwith data: ", list_to_str(\@values); | |
if ($extra->{'sql_only'}) { | |
return get_plain_sql($SQL, @values) . ";\n"; | |
} | |
else { | |
my $sth = get_dbh()->prepare($SQL); | |
$sth->execute(@values) or ERROR $sth->errstr; | |
return $key_field ? $data->{$key_field} : 0; | |
} | |
} | |
else { | |
# insert data | |
my $field_list = join(', ', @fields); | |
my $value_placeholders = '?,'x@values; | |
chop($value_placeholders); | |
my $SQL = qq[ | |
INSERT INTO $tablename ( $field_list ) | |
VALUES ( $value_placeholders ) | |
]; | |
DEBUG '[', caller, "] saving data", Dumper($data), "\nby $SQL \nwith data: ", list_to_str(\@values); | |
if ($extra->{'sql_only'}) { | |
return get_plain_sql($SQL, @values) . ";\n"; | |
} | |
else { | |
my $sth = get_dbh()->prepare($SQL); | |
$sth->execute(@values) or ERROR $sth->errstr; | |
my $new_id = $sth->{mysql_insertid}; | |
return $new_id; | |
} | |
} | |
} | |
sub get_plain_sql { | |
my ($sql, @params) = @_; | |
my @qmarks = $sql =~ /(\?)/g; | |
if (scalar @qmarks != scalar @params) { | |
ERROR "numbers of question marks and params are inconsequent"; | |
return undef; | |
} | |
else { | |
for my $p (@params) { | |
$p =~ s/'/\\'/g; | |
$p =~ s/\?/ /g; | |
$sql =~ s/\?/'$p'/; | |
} | |
return $sql; | |
} | |
} | |
sub list_to_str { | |
my ($data) = @_; | |
no warnings 'uninitialized'; | |
my $result = join(', ', map { qq['$_'] } @$data); | |
return "($result)"; | |
} | |
1; | |
# vim: set et sw=4 ts=4: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment