package Class::Phrasebook::SQL; use Class::Phrasebook; our @ISA = qw (Class::Phrasebook); use strict; use Term::ANSIColor qw(:constants); use bytes; # reset to normal at the end of each line. $Term::ANSIColor::AUTORESET = 1; our $VERSION = '0.87'; #################### # new #################### sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = $class->SUPER::new(@_); $self->{PLACE_HOLDERS_CONATAIN_DOLLARS} = 0; bless ($self, $class); # reconsecrate return $self; } # of new ################################################################### # $statement = get($key, { var1 => $value1, var2 => value2 ... }) # where $key will be the key to certain statement, and var1, var2 # and so on will be $var1 and $var2 in the definition of that # statement in the load method above. ################################################################### sub get { my $self = shift; my $key = shift; my $variables = shift; # the DEBUG_PRINTS is controlled by an environment. my $debug_prints = lc($ENV{PHRASEBOOK_SQL_DEBUG_PRINTS}) || ""; # the SAVE_STATEMENTS_FILE_PATH might be controlled by an environment if ($ENV{PHRASEBOOK_SQL_SAVE_STATEMENTS_FILE_PATH}) { $self->{SAVE_STATEMENTS_FILE_PATH} = $ENV{PHRASEBOOK_SQL_SAVE_STATEMENTS_FILE_PATH}; } if ($self->{PLACE_HOLDERS_CONATAIN_DOLLARS}) { foreach my $key (keys(%$variables)) { $variables->{$key} =~ s/\$/__DOLLAR__/g; } } if ($debug_prints) { if ($debug_prints eq "color") { # check that all the variables defined in $variables foreach my $key (keys(%$variables)) { unless (defined($variables->{$key})) { print "["; print GREEN called_by(); print "]"; print BLUE "["; print RED "$key is not defined"; print BLUE "]\n"; } } } elsif ($debug_prints eq "html") { # check that all the variables defined in $variables foreach my $key (keys(%$variables)) { unless (defined($variables->{$key})) { print "
["; print called_by(); print "]"; print "["; print "$key is not defined"; print "]\n"; } } } elsif ($debug_prints eq "text") { # check that all the variables defined in $variables foreach my $key (keys(%$variables)) { unless (defined($variables->{$key})) { print "["; print called_by(); print "]"; print "["; print "$key is not defined"; print "]\n"; } } } } # of if ($debug_prints) my $statement = $self->{PHRASES}{$key}; unless (defined($statement)) { if ($debug_prints) { if ($debug_prints eq "color") { print RED "No statement for $key\n"; } elsif ($debug_prints eq "html") { print "
No statement for $key". "\n"; } elsif ($debug_prints eq "text") { print "No statement for $key\n"; } } $self->{LOG}->write ("No statement for ".$key."\n", 3); return undef; } # deal with statments that are not update: if ($statement !~ /^\s*update/i) { # process the placeholders if ($debug_prints) { $statement =~ s/\$([a-zA-Z0-9_]+)/debug_print_variable($1, $variables)/ge; $statement =~ s/\$\(([a-zA-Z0-9_]+)\)/debug_print_variable($1, $variables)/ge; } # if the variable is inside quotes - escape quotes inside the variable $statement =~ s/(\'\s*)\$([a-zA-Z0-9_]+)(\s*\')/escape_quotes($self, $1, $variables->{$2}, $3)/ge; # deal with the rest of the variables without escaping the quotes $statement =~ s/\$([a-zA-Z0-9_]+)/$variables->{$1}/g; # also process variables in $(var_name) format. $statement =~ s/\$\(([a-zA-Z0-9_]+)\)/$variables->{$1}/g; } else { # deal with updates in the following way: # devide the statement to before the set, after the where and the rest $statement =~ /(\sset\s)/i; my $statement_before_set = $`.$&; # till and include the set my $pairs = $'; # the rest my $statement_after_where; if ($pairs =~ /(\swhere\s)/i) { # if there is where $statement_after_where = $&.$'; # holds the where and anything # after it $pairs = $`; # holds only the pairs } # now $statement holds only the pairs we set. for each line, if # one of the variables is not defined, we remove that line. my @lines = split(/\n/, $pairs); my @lefted_lines = grep ( is_variables_defined_in_this_line($_, $variables), @lines); # join the lines $pairs = join("", @lefted_lines); # clean the last comma $pairs =~ s/\,\s*$//; # join the parts of the statement again $statement = $statement_before_set.$pairs.$statement_after_where; # if the variable is inside quotes - escape quotes inside the variable $statement =~ s/(\'\s*)\$([a-zA-Z0-9_]+)(\s*\')/escape_quotes($self, $1, $variables->{$2}, $3)/ge; # replace the variables with their values $statement =~ s/\$([a-zA-Z0-9_]+)/$variables->{$1}/g; $statement =~ s/\$\(([a-zA-Z0-9_]+)\)/$variables->{$1}/g; } # now deal with empty brackets of IN - just put instead of that expression # the word TRUE. $statement =~ s/[a-zA-Z0-9_\.]+\s+in\s*\(\s*\)/TRUE/ig; # deal with "= NULL" if ($self->{USE_IS_NULL}) { $statement =~ s/\= *NULL/is NULL/ig; } if ($self->{PLACE_HOLDERS_CONATAIN_DOLLARS}) { $statement =~ s/__DOLLAR__/\$/g; } # remove new lines if needed if ($self->{REMOVE_NEW_LINES}) { $statement =~ s/\n//g; } # debug prints if ($debug_prints) { if ($debug_prints eq "color") { print "["; print GREEN called_by(); print "]"; print RED "["; print BLUE $key; print RED "]\n"; print $statement."\n"; } elsif ($debug_prints eq "html") { print "
["; print "".called_by().""; print "]"; print "["; print "$key"; print "]\n"; print $statement."\n"; } elsif ($debug_prints eq "text") { print "["; print called_by(); print "]"; print "["; print $key; print "]\n"; print $statement."\n"; } } # of if ($debug_prints) # save the statement in a file if the path of such a file is defined. if ($self->{SAVE_STATEMENTS_FILE_PATH}) { open(TMP, ">>".$self->{SAVE_STATEMENTS_FILE_PATH}); my $saved_statement = $statement; $saved_statement =~ s/[\s\n]+/ /g; print TMP $saved_statement.";\n"; close(TMP); } unless ($statement) { if ($debug_prints) { if ($debug_prints eq "color") { print RED "Oops - no statement for $key !!!\n"; } elsif ($debug_prints eq "html") { print "
Oops - no statement for $key". "\n"; } elsif ($debug_prints eq "text") { print "Oops - no statement for $key !!!\n"; } } } return $statement; } # of get ########################### # escape_quotes ########################### # escape ' in the variables sub escape_quotes { my $self = shift; my $quote1 = shift; my $variable = shift; my $quote2 = shift; my $escaped_quote = defined($self->{ESCAPED_QUOTE}) ? $self->{ESCAPED_QUOTE} : "\'\'"; $variable =~ s/\'/$escaped_quote/g; return $quote1.$variable.$quote2; } # of escape_quotes ################# # escaped_quote ################# sub escaped_quote { my $self = shift; if (@_) { $self->{ESCAPED_QUOTE} = shift || "\'\'" } return $self->{ESCAPED_QUOTE}; } # of escaped_quote ################# # use_is_null ################# sub use_is_null { my $self = shift; if (@_) { $self->{USE_IS_NULL} = shift } return $self->{USE_IS_NULL}; } # of use_is_null ############################## # save_statements_file_path ############################## sub save_statements_file_path { my $self = shift; if (@_) { $self->{SAVE_STATEMENTS_FILE_PATH} = shift } return $self->{SAVE_STATEMENTS_FILE_PATH}; } # of save_statements_file_path ################################# # place_holders_conatain_dollars ################################# sub place_holders_conatain_dollars { my $self = shift; if (@_) { $self->{PLACE_HOLDERS_CONATAIN_DOLLARS} = shift; } return $self->{PLACE_HOLDERS_CONATAIN_DOLLARS}; } # of place_holders_conatain_dollars ####################################################### # is_variables_defined_in_this_line($line, $variables) ####################################################### # helper function # gets a a string (line) and reference to hash (that holds the pairs that are # the placeholders. return 1 if all the variables in the string are defined # in the hash. sub is_variables_defined_in_this_line { my $line = shift; my $variables = shift; my $line_tmp = $line; while ($line_tmp =~ /\$([a-zA-Z0-9_]+)/) { unless (defined($variables->{$1})) { return 0; } $line_tmp = $'; } $line_tmp = $line; while ($line_tmp =~ /\$\(([a-zA-Z0-9_]+)\)/) { unless (defined($variables->{$1})) { return 0; } $line_tmp = $'; } return 1; } # of is_variables_defined_in_this_line ######################### # debug_print_variable ######################### # helper function sub debug_print_variable { my $key = shift; my $variables = shift; my $value = $variables->{$key}; my $debug_prints = lc($ENV{PHRASEBOOK_SQL_DEBUG_PRINTS}) || ""; if ($debug_prints eq "color") { print MAGENTA "$key = "; if (defined($value)) { print MAGENTA "$value\n"; } else { print RED "undef\n"; } } elsif ($debug_prints eq "html") { print "
$key = ";
if (defined($value)) {
print "$value\n";
}
else {
print "undef\n";
}
}
elsif ($debug_prints eq "text") {
print "$key = ";
if (defined($value)) {
print "$value\n";
}
else {
print "undef\n";
}
}
return "\$".$key;
} # of debug_print_varibale
#######################
# called_by
#######################
sub called_by {
my $depth = 2;
my $args;
my $pack;
my $file;
my $line;
my $subr;
my $has_args;
my $wantarray;
my $evaltext;
my $is_require;
my $hints;
my $bitmask;
my @subr;
my $str = "";
while ($depth < 7) {
($pack, $file, $line, $subr, $has_args, $wantarray,
$evaltext, $is_require, $hints, $bitmask) = caller($depth);
unless (defined($subr)) {
last;
}
$depth++;
$line = "$file:".$line."-->";
push(@subr, $line.$subr);
}
@subr = reverse(@subr);
foreach $subr (@subr) {
$str .= $subr;
$str .= " > ";
}
$str =~ s/ > $/: /;
return $str;
} # of called_by
__END__
=head1 NAME
Class::Phrasebook::SQL - Implements the Phrasebook pattern for SQL statements.
=head1 SYNOPSIS
use Class::Phrasebook::SQL;
my $sql = new Class::Phrasebook::SQL($log, "test.xml");
$sql->load("Pg");
$statement = $sql->get("INSERT_INTO_CONFIG_ROW",
{ id => 88,
parent => 77,
level => 5 });
=head1 DESCRIPTION
This class inherits from Class::Phrasebook and let us manage all the SQL
code we have in a project, in one file. The is done by placing all the
SQL statements as phrases in the XML file of the Class::Phrasebook.
See I