| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package SQL::Interpolate::Filter; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
625
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
88
|
|
|
4
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
50
|
|
|
5
|
2
|
|
|
2
|
|
2200
|
use Filter::Simple; |
|
|
2
|
|
|
|
|
88234
|
|
|
|
2
|
|
|
|
|
15
|
|
|
6
|
2
|
|
|
|
|
2105
|
use Text::Balanced qw/extract_quotelike |
|
7
|
|
|
|
|
|
|
extract_bracketed |
|
8
|
|
|
|
|
|
|
extract_multiple |
|
9
|
|
|
|
|
|
|
extract_variable |
|
10
|
2
|
|
|
2
|
|
130
|
extract_codeblock/; |
|
|
2
|
|
|
|
|
5
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.32'; |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Source filter. |
|
15
|
|
|
|
|
|
|
# Note: this could be improved as done in the POD of the development 2.0 version of |
|
16
|
|
|
|
|
|
|
# Text::Balanced. |
|
17
|
|
|
|
|
|
|
FILTER { |
|
18
|
|
|
|
|
|
|
my $id = qr/\b(?!([ysm]|q[rqxw]?|tr)\b)\w+/; |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# This lexes the Perl source code, replacing quotelike sql// |
|
21
|
|
|
|
|
|
|
# operators with the result of _process_sql(). |
|
22
|
|
|
|
|
|
|
while ($_ !~ /\G$/gc) { |
|
23
|
|
|
|
|
|
|
my $sql; |
|
24
|
|
|
|
|
|
|
my $last_pos = pos(); |
|
25
|
|
|
|
|
|
|
if (/\G\s+/gc) { } # whitespace |
|
26
|
|
|
|
|
|
|
elsif (/\G#.*/gc) { } # comments |
|
27
|
|
|
|
|
|
|
# sql// operators |
|
28
|
|
|
|
|
|
|
# FIX:should any other quote delimiters be added? |
|
29
|
|
|
|
|
|
|
elsif (/\G\bsql\b\s*(?=[\{\(\[\<\/])/gcs && |
|
30
|
|
|
|
|
|
|
do { |
|
31
|
|
|
|
|
|
|
my $pos = pos(); |
|
32
|
|
|
|
|
|
|
s/\G/ q/; # convert to Perl quote-like |
|
33
|
|
|
|
|
|
|
pos() = $pos; |
|
34
|
|
|
|
|
|
|
$sql = (extract_quotelike())[5]; |
|
35
|
|
|
|
|
|
|
#print ""; |
|
36
|
|
|
|
|
|
|
if (!$sql) { # restore |
|
37
|
|
|
|
|
|
|
s/\G q//; |
|
38
|
|
|
|
|
|
|
pos() = $pos; |
|
39
|
|
|
|
|
|
|
} |
|
40
|
|
|
|
|
|
|
!!$sql; |
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
) |
|
43
|
|
|
|
|
|
|
{ |
|
44
|
|
|
|
|
|
|
my $pos = pos(); |
|
45
|
|
|
|
|
|
|
my $out = _process_sql($sql); |
|
46
|
|
|
|
|
|
|
pos() = $pos; |
|
47
|
|
|
|
|
|
|
substr($_, $last_pos, pos() - $last_pos) = $out; |
|
48
|
|
|
|
|
|
|
pos() = $last_pos + length($out); |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
# prevent things like $y = ... = from being interpreted as string. |
|
51
|
|
|
|
|
|
|
elsif (/\G(?<=[\$\@])\w+/gc) { |
|
52
|
|
|
|
|
|
|
#print "[DEBUG:var:$&]"; |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
elsif (/\G$id/gc) { |
|
55
|
|
|
|
|
|
|
#print "[DEBUG:id:$&]"; |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
elsif (my $next = (extract_quotelike())[0]) { |
|
58
|
|
|
|
|
|
|
#print "[DEBUG:q:$next]"; |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
else { |
|
61
|
|
|
|
|
|
|
/\G./gc; |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
print STDERR "DEBUG:filter[code=$_]" if $SQL::Interpolate::trace_filter; |
|
65
|
|
|
|
|
|
|
}; |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Convert the string inside a sql// quote-like operator into |
|
68
|
|
|
|
|
|
|
# a list of SQL strings and variable references for interpolation. |
|
69
|
|
|
|
|
|
|
sub _process_sql { |
|
70
|
18
|
|
|
18
|
|
38
|
local $_ = shift; |
|
71
|
|
|
|
|
|
|
|
|
72
|
18
|
|
|
|
|
19
|
my @parts; |
|
73
|
18
|
|
|
|
|
22
|
my $instr = 0; |
|
74
|
18
|
|
|
|
|
48
|
while ($_ !~ /\G$/gc) { |
|
75
|
500
|
|
|
|
|
1777
|
my $tok; |
|
76
|
|
|
|
|
|
|
my $tok_type; |
|
77
|
500
|
|
|
|
|
606
|
my $pos_last = pos(); |
|
78
|
500
|
100
|
|
|
|
3095
|
if (/\G(\s+|\*)/gc) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
79
|
102
|
|
|
|
|
1339
|
$tok = $1; |
|
80
|
102
|
|
|
|
|
136
|
$tok_type = 's'; |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
elsif ($tok = (extract_variable($_))[0]) { |
|
83
|
12
|
|
|
|
|
9597
|
$tok_type = 'v'; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
elsif ($tok = (extract_codeblock($_, '{['))[0]) { |
|
86
|
4
|
|
|
|
|
2282
|
$tok_type = 'c'; |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
else { |
|
89
|
382
|
|
|
|
|
66721
|
/\G(.)/gc; |
|
90
|
382
|
|
|
|
|
648
|
$tok = $1; |
|
91
|
382
|
|
|
|
|
506
|
$tok_type = 's'; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
500
|
100
|
|
|
|
1864
|
if ($tok_type eq 's') { |
|
95
|
484
|
100
|
|
|
|
878
|
if ($instr) { |
|
96
|
466
|
|
|
|
|
5272
|
$parts[-1] .= $tok |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
else { |
|
99
|
18
|
|
|
|
|
31
|
push @parts, $tok |
|
100
|
|
|
|
|
|
|
} |
|
101
|
484
|
|
|
|
|
7153
|
$instr = 1; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
else { |
|
104
|
16
|
50
|
|
|
|
67
|
$parts[-1] = 'qq[' . $parts[-1] . ']' if $instr; |
|
105
|
16
|
|
|
|
|
23
|
$instr = 0; |
|
106
|
16
|
100
|
|
|
|
43
|
if ($tok_type eq 'v') { |
|
|
|
50
|
|
|
|
|
|
|
107
|
12
|
|
|
|
|
75
|
push @parts, '\\' . $tok; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
elsif ($tok_type eq 'c') { |
|
110
|
4
|
|
|
|
|
18
|
push @parts, $tok; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
0
|
|
|
|
|
0
|
else { die 'assert'; } |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
} |
|
116
|
18
|
100
|
|
|
|
47
|
$parts[-1] = 'qq[' . $parts[-1] . ']' if $instr; |
|
117
|
|
|
|
|
|
|
|
|
118
|
18
|
|
|
|
|
51
|
my $out = 'SQL::Interpolate::Filter::_make_sql(' |
|
119
|
|
|
|
|
|
|
. join(', ', @parts) . ')'; |
|
120
|
|
|
|
|
|
|
|
|
121
|
18
|
|
|
|
|
72
|
return $out; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# Generated by the sql// operator when source filtering is enabled. |
|
125
|
|
|
|
|
|
|
sub _make_sql { |
|
126
|
18
|
|
|
18
|
|
13360
|
my (@list) = @_; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Note that sql[INSERT INTO mytable $x] gets translated to |
|
129
|
|
|
|
|
|
|
# q[INSERT INTO mytable], \$x |
|
130
|
|
|
|
|
|
|
# regardless whether $x is a scalar or reference since it |
|
131
|
|
|
|
|
|
|
# would be difficult to know at source filtering time whether |
|
132
|
|
|
|
|
|
|
# $x is already a reference. Therefore, we dereference any |
|
133
|
|
|
|
|
|
|
# double reference here (at run-time). |
|
134
|
34
|
100
|
|
|
|
116
|
do { $_ = $$_ if ref($_) eq 'REF' } |
|
135
|
18
|
|
|
|
|
45
|
for @list; |
|
136
|
|
|
|
|
|
|
|
|
137
|
18
|
|
|
|
|
87
|
my $o = SQL::Interpolate::SQL->new(@list); |
|
138
|
18
|
|
|
|
|
89
|
return $o; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
1; |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Implementation Notes: |
|
144
|
|
|
|
|
|
|
# Sub::Quotelike provides similar functionality to this module, |
|
145
|
|
|
|
|
|
|
# but it is not exactly what I need. Sub::Quotelike allows you to |
|
146
|
|
|
|
|
|
|
# replace quote expressions with calls to your own custom function |
|
147
|
|
|
|
|
|
|
# that can return itself and expression. In Sub::Quotelike, the |
|
148
|
|
|
|
|
|
|
# return expression is evaluated within the context of the called |
|
149
|
|
|
|
|
|
|
# subroutine rather that in the scope of the caller as is typically |
|
150
|
|
|
|
|
|
|
# the case with variable interpolation in strings. Therefore, SQL |
|
151
|
|
|
|
|
|
|
# variable interpolation will not work correctly. Furthermore, the |
|
152
|
|
|
|
|
|
|
# current version (0.03) performs fairly simple, and potentially |
|
153
|
|
|
|
|
|
|
# error-prone, source filtering. |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# We also do not utilize "FILTER_ONLY quotelike" in Filter::Simple |
|
156
|
|
|
|
|
|
|
# since its parsing is fairly simplistic and recognizes things like $y |
|
157
|
|
|
|
|
|
|
# = ... = as containing a quote (y=...=). |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
1; |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
__END__ |