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