line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
94306
|
use strict; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
22
|
|
2
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
35
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Footprintless::Plugin::Database::PreparedStatementTemplate; |
5
|
|
|
|
|
|
|
$Footprintless::Plugin::Database::PreparedStatementTemplate::VERSION = '1.06'; |
6
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
39
|
|
7
|
1
|
|
|
1
|
|
4
|
use Carp 'verbose'; |
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
115
|
|
8
|
1
|
|
|
1
|
|
5
|
use Data::Dumper; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
37
|
|
9
|
1
|
|
|
1
|
|
4
|
use Log::Any; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $logger = Log::Any->get_logger(); |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub new { |
14
|
11
|
|
|
11
|
1
|
14153
|
my $self = bless( {}, shift ); |
15
|
11
|
|
|
|
|
41
|
$self->_init(@_); |
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub _init { |
19
|
11
|
|
|
11
|
|
37
|
my ( $self, $sql_template, %bindings ) = @_; |
20
|
|
|
|
|
|
|
my @binding_keys = |
21
|
11
|
50
|
|
|
|
37
|
sort { ( length($b) <=> length($a) ) || ( $a cmp $b ) } |
|
24
|
|
|
|
|
64
|
|
22
|
|
|
|
|
|
|
keys(%bindings); |
23
|
11
|
|
|
|
|
20
|
my @split_text; |
24
|
|
|
|
|
|
|
my @index_to_key; |
25
|
|
|
|
|
|
|
$self->{bindings} = |
26
|
11
|
|
|
|
|
19
|
{ map { $_ => _transform_binding( $_, $bindings{$_} ) } @binding_keys }; |
|
29
|
|
|
|
|
50
|
|
27
|
11
|
|
|
|
|
26
|
_dice( _remove_comments($sql_template), \@split_text, \@index_to_key, @binding_keys ); |
28
|
11
|
|
|
|
|
41
|
$self->{prepared_statement} = join( '?', @split_text ); |
29
|
|
|
|
|
|
|
$self->{parameter_bindings} = |
30
|
11
|
|
|
|
|
18
|
[ map { $self->{bindings}->{$_} } @index_to_key ]; |
|
39
|
|
|
|
|
66
|
|
31
|
|
|
|
|
|
|
|
32
|
11
|
|
|
|
|
18
|
my %used_keys = map { $_ => 1 } @index_to_key; |
|
39
|
|
|
|
|
76
|
|
33
|
11
|
|
|
|
|
21
|
foreach my $unused_key ( grep { !$used_keys{$_} } @binding_keys ) { |
|
29
|
|
|
|
|
53
|
|
34
|
5
|
|
|
|
|
23
|
$logger->warn("Template var [$unused_key] is never used!"); |
35
|
5
|
|
|
|
|
88
|
delete( $self->{bindings}->{$unused_key} ); |
36
|
|
|
|
|
|
|
} |
37
|
11
|
|
|
|
|
50
|
return $self; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub _bind { |
41
|
41
|
|
|
41
|
|
57
|
my ( $binding, $context ) = @_; |
42
|
41
|
100
|
|
|
|
103
|
if ( defined( my $key = $binding->{key} ) ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
43
|
6
|
|
|
|
|
25
|
eval { $binding->{value} = $context->$key() } |
44
|
12
|
100
|
|
|
|
33
|
if ( !defined( $binding->{value} = $context->{$key} ) ); |
45
|
|
|
|
|
|
|
croak( |
46
|
|
|
|
|
|
|
"Cannot bind template var [$binding->{template_key}] - property [$key] cannot be bound in context" |
47
|
12
|
50
|
|
|
|
85
|
) unless defined( $binding->{value} ); |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
elsif ( defined( my $reference = $binding->{reference} ) ) { |
50
|
|
|
|
|
|
|
croak("Cannot bind template var [$binding->{template_key}] - reference to undefined") |
51
|
6
|
50
|
|
|
|
18
|
unless defined( $binding->{value} = $$reference ); |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
elsif ( defined( my $code = $binding->{code} ) ) { |
54
|
|
|
|
|
|
|
croak("Cannot bind template var [$binding->{template_key}] - code returns undefined") |
55
|
10
|
50
|
|
|
|
17
|
unless defined( $binding->{value} = $code->() ); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub _dice { |
60
|
128
|
|
|
128
|
|
218
|
my ( $text, $split_text, $index_to_key, $key, @keys ) = @_; |
61
|
128
|
100
|
|
|
|
192
|
if ( !$key ) { |
62
|
50
|
|
|
|
|
121
|
push( @$split_text, $text ); |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
else { |
65
|
78
|
|
|
|
|
95
|
my $add_ix = 0; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# We need at least one element with a blank string in split... |
68
|
78
|
100
|
|
|
|
433
|
foreach ( $text ? split( /\Q$key\E/, $text, -1 ) : ('') ) { |
69
|
117
|
100
|
|
|
|
229
|
push( @$index_to_key, $key ) if ( $add_ix++ ); |
70
|
117
|
|
|
|
|
173
|
_dice( $_, $split_text, $index_to_key, @keys ); |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub query { |
76
|
16
|
|
|
16
|
1
|
62
|
my ( $self, $context ) = @_; |
77
|
16
|
|
|
|
|
37
|
my $query = { sql => $self->{prepared_statement} }; |
78
|
16
|
50
|
|
|
|
22
|
if ( %{ $self->{bindings} } ) { |
|
16
|
|
|
|
|
34
|
|
79
|
16
|
|
|
|
|
26
|
foreach ( values( %{ $self->{bindings} } ) ) { _bind( $_, $context ) } |
|
16
|
|
|
|
|
40
|
|
|
41
|
|
|
|
|
90
|
|
80
|
|
|
|
|
|
|
$query->{parameters} = |
81
|
16
|
|
|
|
|
25
|
[ map { $_->{value} } @{ $self->{parameter_bindings} } ]; |
|
67
|
|
|
|
|
107
|
|
|
16
|
|
|
|
|
29
|
|
82
|
16
|
|
|
|
|
27
|
foreach ( values( %{ $self->{bindings} } ) ) { _unbind( $_, $context ) } |
|
16
|
|
|
|
|
31
|
|
|
41
|
|
|
|
|
59
|
|
83
|
|
|
|
|
|
|
} |
84
|
16
|
|
|
|
|
95
|
return $query; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub _remove_comments { |
88
|
11
|
|
|
11
|
|
24
|
my ($sql) = @_; |
89
|
11
|
|
|
|
|
27
|
my $sql_out; |
90
|
1
|
50
|
|
1
|
|
7
|
open( my $fh, '>', \$sql_out ) || croak("Cannot write to string!"); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
|
11
|
|
|
|
|
109
|
|
91
|
11
|
|
|
|
|
596
|
my ( $in_block_comment, $in_line_comment, $in_quote ) = ( 0, 0, 0 ); |
92
|
11
|
|
|
|
|
28
|
for ( my $ix = 0; $ix < length($sql); ++$ix ) { |
93
|
1304
|
100
|
|
|
|
1949
|
if ($in_block_comment) { |
|
|
100
|
|
|
|
|
|
94
|
278
|
100
|
|
|
|
516
|
if ( substr( $sql, $ix, 2 ) eq '*/' ) { |
95
|
11
|
|
|
|
|
14
|
$in_block_comment = 0; |
96
|
11
|
|
|
|
|
18
|
++$ix; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
elsif ($in_line_comment) { |
100
|
253
|
100
|
|
|
|
482
|
if ( substr( $sql, $ix, 1 ) eq "\n" ) { |
101
|
10
|
|
|
|
|
12
|
$in_line_comment = 0; |
102
|
10
|
|
|
|
|
20
|
print $fh ("\n"); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
else { |
106
|
773
|
|
|
|
|
1032
|
my $char = substr( $sql, $ix, 1 ); |
107
|
773
|
100
|
100
|
|
|
2742
|
if ( !$in_quote && $char eq '/' && substr( $sql, $ix + 1, 1 ) eq '*' ) { |
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
108
|
11
|
|
|
|
|
15
|
$in_block_comment = 1; |
109
|
11
|
|
|
|
|
17
|
++$ix; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
elsif ( !$in_quote && $char eq '-' && substr( $sql, $ix + 1, 1 ) eq '-' ) { |
112
|
10
|
|
|
|
|
13
|
$in_line_comment = 1; |
113
|
10
|
|
|
|
|
18
|
++$ix; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
else { |
116
|
752
|
100
|
|
|
|
1161
|
$in_quote = !$in_quote if ( $char eq "'" ); |
117
|
752
|
|
|
|
|
1352
|
print $fh ($char); |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
} |
121
|
11
|
|
|
|
|
23
|
close($fh); |
122
|
11
|
|
|
|
|
52
|
return $sql_out; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub _transform_binding { |
126
|
29
|
|
|
29
|
|
46
|
my ( $template_key, $binding ) = @_; |
127
|
29
|
|
|
|
|
41
|
my $ref = ref($binding); |
128
|
29
|
|
|
|
|
53
|
my $new_binding = { template_key => $template_key }; |
129
|
29
|
100
|
100
|
|
|
230
|
if ( my $key = ( ( !$ref && $binding ) || ( $ref eq 'HASH' && $binding->{key} ) ) ) { |
|
|
100
|
100
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
50
|
66
|
|
|
|
|
130
|
6
|
|
|
|
|
7
|
$new_binding->{key} = $key; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
elsif ( my $reference = |
133
|
|
|
|
|
|
|
( ( $ref eq 'SCALAR' && $binding ) || ( $ref eq 'HASH' && $binding->{reference} ) ) ) |
134
|
|
|
|
|
|
|
{ |
135
|
3
|
50
|
|
|
|
7
|
croak( |
136
|
|
|
|
|
|
|
"Template var [$template_key] - 'reference' property of binding is not a 'SCALAR' ref" |
137
|
|
|
|
|
|
|
) unless ref($reference) eq 'SCALAR'; |
138
|
3
|
|
|
|
|
6
|
$new_binding->{reference} = $reference; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
elsif ( $ref eq 'HASH' && defined( $binding->{value} ) ) { |
141
|
|
|
|
|
|
|
croak("'Template var [$template_key] - value' property of binding is a ref") |
142
|
15
|
50
|
|
|
|
28
|
if ref( $binding->{value} ); |
143
|
15
|
|
|
|
|
22
|
$new_binding->{value} = $binding->{value}; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
elsif ( my $code = |
146
|
|
|
|
|
|
|
( ( $ref eq 'CODE' && $binding ) || ( $ref eq 'HASH' && $binding->{code} ) ) ) |
147
|
|
|
|
|
|
|
{ |
148
|
5
|
50
|
|
|
|
12
|
croak("Template var [$template_key] - 'code' property of binding is not a 'CODE' ref") |
149
|
|
|
|
|
|
|
unless ref($code) eq 'CODE'; |
150
|
5
|
|
|
|
|
8
|
$new_binding->{code} = $code; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
else { |
153
|
0
|
|
|
|
|
0
|
croak( "Template var [$template_key] - binding [%s] is invalid", Dumper($binding) ); |
154
|
|
|
|
|
|
|
} |
155
|
29
|
|
|
|
|
70
|
return $new_binding; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub _unbind { |
159
|
41
|
|
|
41
|
|
63
|
my ($binding) = @_; |
160
|
|
|
|
|
|
|
delete( $binding->{value} ) |
161
|
|
|
|
|
|
|
if defined( $binding->{key} ) |
162
|
|
|
|
|
|
|
|| defined( $binding->{reference} ) |
163
|
41
|
100
|
100
|
|
|
153
|
|| defined( $binding->{code} ); |
|
|
|
100
|
|
|
|
|
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
1; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
__END__ |