File Coverage

blib/lib/DBIx/Squirrel/st.pm
Criterion Covered Total %
statement 142 152 93.4
branch 53 68 77.9
condition 5 9 55.5
subroutine 25 25 100.0
pod 0 9 0.0
total 225 263 85.5


line stmt bran cond sub pod time code
1 9     9   73 use strict;
  9         32  
  9         339  
2 9     9   40 use warnings;
  9         17  
  9         387  
3 9     9   160 use 5.010_001;
  9         26  
4              
5             package # hide from PAUSE
6             DBIx::Squirrel::st;
7              
8             require Digest::SHA;
9              
10 9     9   47 use Exporter ();
  9         13  
  9         306  
11 9     9   40 use Sub::Name 'subname';
  9         26  
  9         572  
12 9         936 use DBIx::Squirrel::util qw(
13             confessf
14             cluckf
15 9     9   5008 );
  9         43  
16 9     9   73 use namespace::clean;
  9         23  
  9         84  
17              
18 9     9   2901 use constant E_EXP_STH => 'Expected a statement handle';
  9         23  
  9         647  
19 9     9   53 use constant E_INVALID_PLACEHOLDER => 'Cannot bind invalid placeholder (%s)';
  9         19  
  9         521  
20 9         1368 use constant W_ODD_NUMBER_OF_ARGS =>
21 9     9   49 'Check bind values match placeholder scheme';
  9         19  
22              
23             BEGIN {
24             require DBIx::Squirrel
25 9 50   9   56 unless keys %DBIx::Squirrel::;
26 9         29 *DBIx::Squirrel::st::VERSION = *DBIx::Squirrel::VERSION;
27 9         282 @DBIx::Squirrel::st::ISA = qw(
28             DBI::st
29             Exporter
30             );
31 9         15499 %DBIx::Squirrel::st::EXPORT_TAGS = ( all => [
32             @DBIx::Squirrel::st::EXPORT_OK = qw(
33             statement_digest
34             statement_normalise
35             statement_study
36             statement_trim
37             )
38             ] );
39             }
40              
41             our $FINISH_ACTIVE_BEFORE_EXECUTE = !!1;
42             our $STATEMENT_DIGEST = sub {
43             goto &Digest::SHA::sha256_base64;
44             };
45              
46             sub _private_state {
47 75     75   168 my $self = shift;
48 75 100       527 $self->{private_ekorn} = {} unless defined $self->{private_ekorn};
49 75 100       318 unless (@_) {
50 59 50       110 return $self->{private_ekorn}, $self if wantarray;
51 59         300 return $self->{private_ekorn};
52             }
53 16 50       61 unless ( defined $_[0] ) {
54 0         0 delete $self->{private_ekorn};
55 0         0 shift;
56             }
57 16 50       40 if (@_) {
58 16 50       63 if ( UNIVERSAL::isa( $_[0], 'HASH' ) ) {
59 16         26 $self->{private_ekorn} = { %{ $self->{private_ekorn} }, %{ $_[0] } };
  16         86  
  16         121  
60             }
61             else {
62 0         0 $self->{private_ekorn} = { %{ $self->{private_ekorn} }, @_ };
  0         0  
63             }
64             }
65 16         82 return $self;
66             }
67              
68             sub _placeholders_confirm_positional {
69 25     25   37 my $self = shift;
70 25         49 my $placeholders = $self->_private_state->{Placeholders};
71 25         34 my @placeholders = values %{$placeholders};
  25         69  
72 25         45 my $total_count = @placeholders;
73 25         54 my $count = do {
74 25         32 local($_);
75 25         50 grep { m/^[\:\$\?]\d+$/ } @placeholders;
  19         151  
76             };
77 25 100       75 return unless $count == $total_count;
78 9         31 return $placeholders;
79             }
80              
81             sub _placeholders_map_to_values {
82 8     8   12 my $self = shift;
83 8         17 my $positional = $self->_placeholders_confirm_positional;
84 8         11 my @mappings = do {
85 8         13 local($_);
86 8 50       18 if ($positional) {
87 0         0 map { ( $positional->{$_} => $_[ $_ - 1 ] ) } keys %{$positional};
  0         0  
  0         0  
88             }
89             else {
90 8 100       74 if ( UNIVERSAL::isa( $_[0], 'HASH' ) ) {
91 2         3 %{ $_[0] };
  2         7  
92             }
93             else {
94 6 100       18 if ( UNIVERSAL::isa( $_[0], 'ARRAY' ) ) {
95 2 50 33     4 cluckf W_ODD_NUMBER_OF_ARGS unless @{ $_[0] } && @{ $_[0] } % 2 == 0;
  2         9  
  2         10  
96 2         4 @{ $_[0] };
  2         7  
97             }
98             else {
99 4 100 66     74 cluckf W_ODD_NUMBER_OF_ARGS unless @_ && @_ % 2 == 0;
100 4         1308 @_;
101             }
102             }
103             }
104             };
105 8 50       78 return wantarray ? @mappings : \@mappings;
106             }
107              
108             sub bind {
109 17     17 0 28 my $self = shift;
110 17 50       44 if (@_) {
111 17 100       46 if ( $self->_placeholders_confirm_positional ) {
112 9 50       29 if ( UNIVERSAL::isa( $_[0], 'ARRAY' ) ) {
113 0         0 $self->bind_param( $_, $_[0][ $_ - 1 ] ) for 1 .. scalar @{ $_[0] };
  0         0  
114             }
115             else {
116 9         48 $self->bind_param( $_, $_[ $_ - 1 ] ) for 1 .. scalar @_;
117             }
118             }
119             else {
120 8 50       14 if ( my %kv = @{ $self->_placeholders_map_to_values(@_) } ) {
  8         21  
121 8         150 while ( my( $k, $v ) = each %kv ) {
122 8 100       38 if ( $k =~ m/^[\:\$\?]?(?\d+)$/ ) {
123 1 50       16 confessf E_INVALID_PLACEHOLDER, $k unless $+{bind_id};
124 1         11 $self->bind_param( $+{bind_id}, $v );
125             }
126             else {
127 7         22 $self->bind_param( $k, $v );
128             }
129             }
130             }
131             }
132             }
133 17         35 return $self;
134             }
135              
136             sub bind_param {
137 17     17 0 27 my $self = shift;
138 17         31 my @args = do {
139 17         41 my( $param, $value, @attr ) = @_;
140 17         39 my $placeholders = $self->_private_state->{Placeholders};
141 17 50       47 if ($placeholders) {
142 17 100       87 if ( $param =~ m/^[\:\$\?]?(?\d+)$/ ) {
143 10         87 $+{bind_id}, $value, @attr;
144             }
145             else {
146 7         12 local($_);
147 7         12 map { ( $_, $value, @attr ) } do {
  7         30  
148 7 100       23 if ( $param =~ m/^[\:\$\?]/ ) {
149 4         8 grep { $placeholders->{$_} eq $param } keys %{$placeholders};
  4         15  
  4         15  
150             }
151             else {
152 3         6 grep { $placeholders->{$_} eq ":$param" } keys %{$placeholders};
  3         13  
  3         9  
153             }
154             };
155             }
156             }
157             else {
158 0         0 $param, $value, @attr;
159             }
160             };
161 17 50       166 return unless $self->SUPER::bind_param(@args);
162 17 50       91 return wantarray ? @args : \@args;
163             }
164              
165             sub execute {
166 22     22 0 6796 my $self = shift;
167 22 100 66     308 $self->finish if $FINISH_ACTIVE_BEFORE_EXECUTE && $self->{Active};
168 22 100       113 $self->bind(@_) if @_;
169 22         711 return $self->SUPER::execute;
170             }
171              
172             sub iterate {
173 2     2 0 11 return DBIx::Squirrel::it->new(@_);
174             }
175              
176             BEGIN {
177 9     9   124 *iterator = subname( iterator => \&iterate );
178 9         50 *itor = subname( itor => \&iterate );
179 9         1033 *it = subname( it => \&iterate );
180             }
181              
182             sub results {
183 2     2 0 18 return DBIx::Squirrel::rs->new(@_);
184             }
185              
186             BEGIN {
187 9     9   72 *resultset = subname( resultset => \&results );
188 9         96 *rset = subname( rset => \&results );
189 9         7015 *rs = subname( rs => \&results );
190             }
191              
192             sub statement_digest {
193 30     30 0 74 return $STATEMENT_DIGEST->(@_);
194             }
195              
196             sub statement_normalise {
197 31     31 0 87 my $statement = statement_trim(shift);
198 30         48 my $normalised = $statement;
199 30         85 $normalised =~ s{[\:\$\?]\w+\b}{?}g;
200 30         78 return $normalised, $statement, statement_digest($statement);
201             }
202              
203             sub statement_study {
204 31     31 0 4835 my( $normal, $trimmed, $digest ) = statement_normalise(shift);
205 30 100       106 return unless length $trimmed;
206 29         39 my %positions_to_params_map = do {
207 29 100       126 if ( my @params = $trimmed =~ m{[\:\$\?]\w+\b}g ) {
208 14         21 local($_);
209 14         38 map { 1 + $_ => $params[$_] } 0 .. $#params;
  20         81  
210             }
211             else {
212 15         30 ();
213             }
214             };
215 29         218 return \%positions_to_params_map, $normal, $trimmed, $digest;
216             }
217              
218             sub statement_trim {
219 39     39 0 245896 my $statement = do {
220 39 100       82 if ( ref $_[0] ) {
221 3 100       38 if ( UNIVERSAL::isa( $_[0], 'DBIx::Squirrel::st' ) ) {
    100          
222 1         5 shift->_private_state->{OriginalStatement};
223             }
224             elsif ( UNIVERSAL::isa( $_[0], 'DBI::st' ) ) {
225 1         17 shift->{Statement};
226             }
227             else {
228 1         7 confessf(E_EXP_STH);
229             }
230             }
231             else {
232 36 100       86 defined $_[0] ? shift : '';
233             }
234             };
235 38         105 $statement =~ s{\s+--\s+.*$}{}gm;
236 38         102 $statement =~ s{^[[:blank:]\r\n]+}{}gm;
237 38         132 $statement =~ s{[[:blank:]\r\n]+$}{}gm;
238 38 100       174 return $statement =~ m/\S/ ? $statement : '';
239             }
240              
241             1;