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__ |