line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
43829
|
use 5.006; |
|
1
|
|
|
|
|
4
|
|
2
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
119
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package SQL::Interpol; |
5
|
|
|
|
|
|
|
$SQL::Interpol::VERSION = '1.102'; |
6
|
|
|
|
|
|
|
# ABSTRACT: interpolate Perl variables into SQL statements |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
1304
|
use Exporter::Tidy all => [ qw( sql_interp sql ) ]; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
6
|
|
9
|
|
|
|
|
|
|
|
10
|
23
|
|
|
23
|
1
|
16866
|
sub sql { bless [ @_ ], __PACKAGE__ } |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub sql_interp { |
13
|
64
|
|
|
64
|
1
|
35469
|
my $p = SQL::Interpol::Parser->new; |
14
|
64
|
|
|
|
|
460
|
my $sql = $p->parse( @_ ); |
15
|
58
|
|
|
|
|
1320
|
my $bind = $p->bind; |
16
|
58
|
|
|
|
|
1002
|
return ( $sql, @$bind ); |
17
|
|
|
|
|
|
|
} |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
package SQL::Interpol::Parser; |
21
|
|
|
|
|
|
|
$SQL::Interpol::Parser::VERSION = '1.102'; |
22
|
1
|
|
|
1
|
|
811
|
use Object::Tiny::Lvalue qw( alias_id bind ); |
|
1
|
|
|
|
|
358
|
|
|
1
|
|
|
|
|
4
|
|
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
168
|
use Carp (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
25
|
|
|
|
|
|
|
|
26
|
1
|
|
|
1
|
|
5
|
use constant IDENT_RX => qr/[a-zA-Z_][a-zA-Z0-9_\$\.]*/; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
79
|
|
27
|
1
|
|
|
1
|
|
5
|
use constant VALID => { ARRAY => 1, SCALAR => 1, 'SQL::Interpol' => 1, '' => 1 }; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1393
|
|
28
|
|
|
|
|
|
|
|
29
|
6
|
|
|
6
|
|
776
|
sub _error { Carp::croak 'SQL::Interpol error: ', @_ } |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub new { |
32
|
64
|
|
|
64
|
|
97
|
my $class = shift; |
33
|
64
|
|
|
|
|
218
|
$class->SUPER::new( alias_id => 0, bind => [] ); |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub parse { |
37
|
77
|
|
|
77
|
|
101
|
my $self = shift; |
38
|
|
|
|
|
|
|
|
39
|
77
|
|
|
|
|
112
|
my $sql = ''; |
40
|
77
|
|
|
|
|
1736
|
my $bind = $self->bind; |
41
|
|
|
|
|
|
|
|
42
|
77
|
|
|
|
|
275
|
my ( $item, $prev ); |
43
|
|
|
|
|
|
|
my $error = sub { |
44
|
0
|
0
|
|
0
|
|
0
|
my $where = defined $prev ? " following '$prev'" : ''; |
45
|
0
|
|
|
|
|
0
|
_error "Unrecognized element '$item'$where"; |
46
|
77
|
|
|
|
|
293
|
}; |
47
|
|
|
|
|
|
|
|
48
|
77
|
|
|
|
|
198
|
while ( @_ ) { |
49
|
164
|
|
|
|
|
251
|
$item = shift @_; |
50
|
164
|
|
|
|
|
250
|
my $type = ref $item; |
51
|
164
|
|
|
|
|
233
|
my $append; |
52
|
|
|
|
|
|
|
|
53
|
164
|
100
|
|
|
|
345
|
if ( 'SQL::Interpol' eq $type ) { |
54
|
23
|
|
|
|
|
53
|
unshift @_, @$item; |
55
|
23
|
|
|
|
|
56
|
next; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
141
|
100
|
|
|
|
759
|
if ( not $type ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
59
|
73
|
|
|
|
|
134
|
$prev = $append = $item; |
60
|
|
|
|
|
|
|
} |
61
|
1
|
|
|
|
|
49
|
elsif ( $sql =~ s/(\s*${(IDENT_RX)}\s+(NOT\s+)?IN)\s*$//oi ) { |
62
|
|
|
|
|
|
|
my @value |
63
|
8
|
50
|
33
|
|
|
36
|
= 'SCALAR' eq $type ? $$item |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
64
|
|
|
|
|
|
|
: 'ARRAY' eq $type ? @$item |
65
|
|
|
|
|
|
|
: 'REF' eq $type && 'ARRAY' eq ref $$item ? @$$item |
66
|
|
|
|
|
|
|
: $error->(); |
67
|
8
|
|
66
|
|
|
26
|
my $list = @value && join ', ', $self->bind_or_parse_values( @value ); |
68
|
8
|
100
|
|
|
|
56
|
$append = @value ? "$1 ($list)" : $2 ? '1=1' : '1=0'; |
|
|
100
|
|
|
|
|
|
69
|
|
|
|
|
|
|
} |
70
|
1
|
|
|
|
|
94
|
elsif ( $sql =~ /\b(REPLACE|INSERT)[\w\s]*\sINTO\s*${(IDENT_RX)}\s*$/oi ) { |
71
|
|
|
|
|
|
|
my @value |
72
|
|
|
|
|
|
|
= 'SCALAR' eq $type ? $$item |
73
|
|
|
|
|
|
|
: 'ARRAY' eq $type ? @$item |
74
|
10
|
50
|
|
|
|
39
|
: 'HASH' eq $type ? do { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
75
|
3
|
|
|
|
|
14
|
my @key = sort keys %$item; |
76
|
3
|
|
|
|
|
8
|
my $list = join ', ', @key; |
77
|
3
|
|
|
|
|
6
|
$append = "($list) "; |
78
|
3
|
|
|
|
|
8
|
@$item{ @key }; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
: $error->(); |
81
|
10
|
100
|
|
|
|
37
|
my $list = @value ? join ', ', $self->bind_or_parse_values( @value ) : ''; |
82
|
10
|
|
|
|
|
49
|
$append .= "VALUES($list)"; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
elsif ( 'SCALAR' eq $type ) { |
85
|
14
|
|
|
|
|
28
|
push @$bind, $$item; |
86
|
14
|
|
|
|
|
19
|
$append = '?'; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
elsif ( 'HASH' eq $type ) { # e.g. WHERE {x = 3, y = 4} |
89
|
10
|
100
|
|
|
|
50
|
if ( $sql =~ /\b(?:ON\s+DUPLICATE\s+KEY\s+UPDATE|SET)\s*$/i ) { |
|
|
100
|
|
|
|
|
|
90
|
2
|
50
|
|
|
|
7
|
_error 'Hash has zero elements.' if not keys %$item; |
91
|
2
|
|
|
|
|
10
|
my @k = sort keys %$item; |
92
|
2
|
|
|
|
|
7
|
my @v = $self->bind_or_parse_values( @$item{ @k } ); |
93
|
2
|
|
|
|
|
23
|
$append = join ', ', map "$k[$_]=$v[$_]", 0 .. $#k; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
elsif ( not keys %$item ) { |
96
|
1
|
|
|
|
|
2
|
$append = '1=1'; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
else { |
99
|
|
|
|
|
|
|
my $cond = join ' AND ', map { |
100
|
7
|
|
|
|
|
21
|
my $expr = $_; |
|
10
|
|
|
|
|
14
|
|
101
|
10
|
|
|
|
|
17
|
my $eval = $item->{ $expr }; |
102
|
|
|
|
|
|
|
( not defined $eval ) ? "$expr IS NULL" |
103
|
5
|
|
|
|
|
40
|
: 'ARRAY' ne ref $eval ? map { "$expr=$_" } $self->bind_or_parse_values( $eval ) |
104
|
10
|
100
|
|
|
|
38
|
: do { |
|
|
100
|
|
|
|
|
|
105
|
3
|
100
|
|
|
|
9
|
@$eval ? do { |
106
|
2
|
|
|
|
|
8
|
my $list = join ', ', $self->bind_or_parse_values( @$eval ); |
107
|
2
|
|
|
|
|
14
|
"$expr IN ($list)"; |
108
|
|
|
|
|
|
|
} : '1=0'; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} sort keys %$item; |
111
|
6
|
100
|
|
|
|
24
|
$cond = "($cond)" if keys %$item > 1; |
112
|
6
|
|
|
|
|
10
|
$append = $cond; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
elsif ( 'ARRAY' eq $type ) { # result set |
116
|
26
|
100
|
|
|
|
55
|
_error 'table reference has zero rows' if not @$item; # improve? |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# e.g. [[1,2],[3,4]] or [{a=>1,b=>2},{a=>3,b=>4}]. |
119
|
25
|
|
100
|
|
|
156
|
my $do_alias = $sql =~ /(?:\bFROM|JOIN)\s*$/i && ( $_[0] || '' ) !~ /\s*AS\b/i; |
120
|
|
|
|
|
|
|
|
121
|
25
|
|
|
|
|
34
|
my $row0 = $item->[0]; |
122
|
25
|
|
|
|
|
39
|
my $type0 = ref $row0; |
123
|
|
|
|
|
|
|
|
124
|
25
|
100
|
|
|
|
55
|
if ( 'ARRAY' eq $type0 ) { |
|
|
50
|
|
|
|
|
|
125
|
15
|
100
|
|
|
|
31
|
_error 'table reference has zero columns' if not @$row0; # improve? |
126
|
|
|
|
|
|
|
$append = join ' UNION ALL ', map { |
127
|
13
|
|
|
|
|
24
|
'SELECT ' . join ', ', $self->bind_or_parse_values( @$_ ); |
|
15
|
|
|
|
|
51
|
|
128
|
|
|
|
|
|
|
} @$item; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
elsif ( 'HASH' eq $type0 ) { |
131
|
10
|
100
|
|
|
|
28
|
_error 'table reference has zero columns' if not keys %$row0; # improve? |
132
|
8
|
|
|
|
|
27
|
my @k = sort keys %$row0; |
133
|
|
|
|
|
|
|
$append = join ' UNION ALL ', do { |
134
|
8
|
|
|
|
|
24
|
my @v = $self->bind_or_parse_values( @$row0{ @k } ); |
135
|
8
|
|
|
|
|
84
|
'SELECT ' . join ', ', map "$v[$_] AS $k[$_]", 0 .. $#k; |
136
|
|
|
|
|
|
|
}, map { |
137
|
8
|
|
|
|
|
14
|
'SELECT ' . join ', ', $self->bind_or_parse_values( @$_{ @k } ); |
|
2
|
|
|
|
|
6
|
|
138
|
|
|
|
|
|
|
} @$item[ 1 .. $#$item ]; |
139
|
|
|
|
|
|
|
} |
140
|
0
|
|
|
|
|
0
|
else { $error->() } |
141
|
|
|
|
|
|
|
|
142
|
21
|
|
|
|
|
109
|
$append = "($append)"; |
143
|
21
|
100
|
|
|
|
342
|
$append .= ' AS tbl' . $self->alias_id++ if $do_alias; |
144
|
|
|
|
|
|
|
} |
145
|
0
|
|
|
|
|
0
|
else { $error->() } |
146
|
|
|
|
|
|
|
|
147
|
135
|
50
|
|
|
|
368
|
next if not defined $append; |
148
|
135
|
100
|
100
|
|
|
584
|
$sql .= ' ' if $sql =~ /\S/ and $append !~ /\A\s/; |
149
|
135
|
|
|
|
|
373
|
$sql .= $append; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
71
|
|
|
|
|
360
|
return $sql; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# interpolate values from aggregate variable (hashref or arrayref) |
156
|
|
|
|
|
|
|
sub bind_or_parse_values { |
157
|
48
|
|
|
48
|
|
64
|
my $self = shift; |
158
|
|
|
|
|
|
|
map { |
159
|
48
|
|
|
|
|
85
|
my $type = ref; |
|
74
|
|
|
|
|
203
|
|
160
|
74
|
100
|
|
|
|
170
|
_error "unrecognized $type value in aggregate" unless VALID->{ $type }; |
161
|
73
|
100
|
|
|
|
154
|
$type ? $self->parse( $_ ) : ( '?', push @{ $self->bind }, $_ )[0]; |
|
60
|
|
|
|
|
1340
|
|
162
|
|
|
|
|
|
|
} @_; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
undef *VALID; |
166
|
|
|
|
|
|
|
undef *IDENT_RX; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
1; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
__END__ |