Using placeholders are a must in SQL!
#prepare my $sth = $dbh->prepare('SELECT * FROM people WHERE lastname = ? AND f +irstname = ?'); #execute with list of bindvars $sth->execute( $lastname, $firstname );
But it's a bit cumbersome to adjust the bind values if the order changes.
It's even more work if you have to use an array of values like inside an IN ( ?, ?, ?) operation.
I started to hack something to auto-generate placeholders, for a string passed inside a code-block:
Parsing the output of B::Deparse is even more fragile than I thought, the next version will walk the OP-Tree directly. (For instance parsing multiline SQL doesn't work yet.)
I'm not yet sure how to combine this in the best way with DBI.
This is a one days job in the sense of "release often".
Comments?
Hmmm ... I can probably avoid the hassle of parsing the OP-tree by tieing the variables ...
use strict; use warnings; use B::Deparse; use PadWalker qw/closed_over peek_sub set_closed_over/; use Data::Dump qw/pp/; # ========= Tests use Test::More; # lexicals for placeholders my $a = 'A'; my @list = qw/L I S T/; my $x = 'X'; # no placeholders for underscore vars my @_table = "any_table"; my $sql = sub { "SELECT * FROM @_table WHERE a = $a AND b IN (@list) A +ND c = $x" }; my @stm = holderplace($sql); is_deeply( \@stm, [ "SELECT * FROM any_table WHERE a = ? AND b IN (?, ?, ?, ? +) AND c = ?", [\"A", ["L", "I", "S", "T"], \"X"] ], "statement with placeholders plus bind variables" ); # change bind variables $a = 'AA'; @list = qw/LL II SS TT/; $x = 'XX'; is_deeply( \@stm, [ "SELECT * FROM any_table WHERE a = ? AND b IN (?, ?, ?, ? +) AND c = ?", [\"AA", ["LL", "II", "SS", "TT"], \"XX"] ], "statement with placeholders plus changed variables" ); done_testing(); # ========== Code sub holderplace { my ($lambda)=@_; my $h_vars = closed_over($lambda); my %new_vars; my @value_refs; for my $key ( keys %$h_vars) { my $sigil = substr $key,0,1; # exclude variables starting with _ next if $key =~ m/^\Q${sigil}\E_/; if ( '$' eq $sigil ) { $new_vars{$key} = \'?'; } elsif ( '@' eq $sigil ) { $new_vars{$key} = [ join ", ", ("?") x @{$h_vars->{$key} } ]; } else { next; # Error? } } # Create Statement with placeholders set_closed_over( $lambda, \%new_vars ); my $newstr = $lambda->(); # Variable refs in order of placeholders my @var_refs = map { $h_vars->{$_} } grep { $new_vars{$_} } @{ get_vars($lambda) }; return ("$newstr", \@var_refs ); } sub get_vars { # scans output of B::Deparse to get interpolated vars in order my ($lambda)=@_; # deparse sub body my $source = B::Deparse->new('-q')->coderef2text($lambda); # returns something like: # { # use warnings; # use strict; # 'SELECT * FROM ' . join($", @_table) . ' WHERE x = ' . $a . ' AN +D b IN (' . join($", @list) . ') ' . $x; # } # truncate {block} and use statements $source =~ s/^{\s*(use.*?;\s*)*//s; $source =~ s/;\s*}$//s; #warn $source; my %quotes = qw"[ ] ( ) < > { } / /"; $quotes{'#'}='#'; # single quotes like q(...) my $re_q = join "|", map { "q\\$_.*?\\$quotes{$_}" } keys %quotes; #warn pp my @parts = split /\s* (?: '(?:\\'|[^'])*?' | $re_q )\s*/msx, $so +urce; for my $part (@parts) { next unless $part =~ /^\..*\.?$/; if ( $part =~ /^\. join\(.*? (\@\w+)\)( \.)?$/) { $part = $1; # array } elsif ( $part =~ /^\. (\$\w+)( \.)?$/) { $part = $1; # scalar } } return \@parts; }
Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
Wikisyntax for the Monastery
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: RFC: Placeholder creation for SQL statements
by Eily (Monsignor) on Mar 09, 2018 at 16:20 UTC | |
by LanX (Saint) on Mar 09, 2018 at 16:43 UTC | |
by Eily (Monsignor) on Mar 09, 2018 at 18:40 UTC | |
by LanX (Saint) on Mar 09, 2018 at 19:20 UTC | |
by Eily (Monsignor) on Mar 10, 2018 at 00:27 UTC | |
| |
|
Re: RFC: Placeholder creation for SQL statements
by roboticus (Chancellor) on Mar 09, 2018 at 22:10 UTC | |
by LanX (Saint) on Mar 10, 2018 at 14:11 UTC | |
|
Re: RFC: Placeholder creation for SQL statements
by LanX (Saint) on Mar 09, 2018 at 17:34 UTC | |
|
Re: RFC: Placeholder creation for SQL statements
by erix (Prior) on Aug 29, 2022 at 11:34 UTC | |
by kikuchiyo (Hermit) on Aug 29, 2022 at 12:23 UTC | |
by erix (Prior) on Aug 29, 2022 at 13:36 UTC | |
by kikuchiyo (Hermit) on Aug 29, 2022 at 14:34 UTC | |
by LanX (Saint) on Aug 29, 2022 at 21:19 UTC | |
| A reply falls below the community's threshold of quality. You may see it by logging in. | |
| A reply falls below the community's threshold of quality. You may see it by logging in. |