line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::SecureCGI; |
2
|
|
|
|
|
|
|
|
3
|
21
|
|
|
21
|
|
723230
|
use warnings; |
|
21
|
|
|
|
|
46
|
|
|
21
|
|
|
|
|
838
|
|
4
|
21
|
|
|
21
|
|
93
|
use strict; |
|
21
|
|
|
|
|
28
|
|
|
21
|
|
|
|
|
588
|
|
5
|
21
|
|
|
21
|
|
1904
|
use utf8; |
|
21
|
|
|
|
|
53
|
|
|
21
|
|
|
|
|
124
|
|
6
|
21
|
|
|
21
|
|
575
|
use feature ':5.10'; |
|
21
|
|
|
|
|
27
|
|
|
21
|
|
|
|
|
1905
|
|
7
|
21
|
|
|
21
|
|
90
|
use Carp; |
|
21
|
|
|
|
|
29
|
|
|
21
|
|
|
|
|
1438
|
|
8
|
|
|
|
|
|
|
|
9
|
21
|
|
|
21
|
|
9981
|
use version; our $VERSION = qv('2.0.6'); # REMINDER: update Changes |
|
21
|
|
|
|
|
33183
|
|
|
21
|
|
|
|
|
112
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# REMINDER: update dependencies in Build.PL |
12
|
21
|
|
|
21
|
|
6532
|
use DBI; |
|
21
|
|
|
|
|
47512
|
|
|
21
|
|
|
|
|
125222
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
## no critic (ProhibitPostfixControls Capitalization ProhibitEnumeratedClasses) |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $PRIVATE = 'private_' . __PACKAGE__; |
18
|
|
|
|
|
|
|
my $INT = qr/\A-?\d+\s+(?:SECOND|MINUTE|HOUR|DAY|MONTH|YEAR)\z/msi; |
19
|
|
|
|
|
|
|
my $IDENT = qr/((?!__)\w[a-zA-Z0-9]*(?:_(?!_)[a-zA-Z0-9]*)*)/ms; |
20
|
|
|
|
|
|
|
my %Func = (); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
DefineFunc(eq => sub { |
24
|
|
|
|
|
|
|
my ($dbh, $f, $v) = @_; |
25
|
|
|
|
|
|
|
my (@val, $null, @expr); |
26
|
|
|
|
|
|
|
@val = ref $v ? @{$v} : $v; |
27
|
|
|
|
|
|
|
$null = grep {!defined} @val; |
28
|
|
|
|
|
|
|
@val = grep {defined} @val; |
29
|
|
|
|
|
|
|
push @expr, sprintf '%s IS NULL', $f if $null; |
30
|
|
|
|
|
|
|
push @expr, sprintf '%s = %s', $f, $dbh->quote($val[0]) if @val==1; |
31
|
|
|
|
|
|
|
push @expr, sprintf '%s IN (%s)', |
32
|
|
|
|
|
|
|
$f, join q{,}, map { $dbh->quote($_) } @val if @val>1; |
33
|
|
|
|
|
|
|
push @expr, 'NOT 1' if !@expr; |
34
|
|
|
|
|
|
|
return @expr==1 ? $expr[0] : '('.join(' OR ', @expr).')'; |
35
|
|
|
|
|
|
|
}); |
36
|
|
|
|
|
|
|
DefineFunc(ne => sub { |
37
|
|
|
|
|
|
|
my ($dbh, $f, $v) = @_; |
38
|
|
|
|
|
|
|
my (@val, $null, @expr); |
39
|
|
|
|
|
|
|
@val = ref $v ? @{$v} : $v; |
40
|
|
|
|
|
|
|
$null = grep {!defined} @val; |
41
|
|
|
|
|
|
|
@val = grep {defined} @val; |
42
|
|
|
|
|
|
|
push @expr, sprintf '%s IS NOT NULL', $f if $null && !@val; |
43
|
|
|
|
|
|
|
push @expr, sprintf '%s IS NULL', $f if !$null && @val; |
44
|
|
|
|
|
|
|
push @expr, sprintf '%s != %s', $f,$dbh->quote($val[0]) if @val==1; |
45
|
|
|
|
|
|
|
push @expr, sprintf '%s NOT IN (%s)', $f, |
46
|
|
|
|
|
|
|
join q{,}, map { $dbh->quote($_) } @val if @val>1; |
47
|
|
|
|
|
|
|
push @expr, 'NOT 0' if !@expr; |
48
|
|
|
|
|
|
|
return @expr==1 ? $expr[0] : '('.join(' OR ', @expr).')'; |
49
|
|
|
|
|
|
|
}); |
50
|
|
|
|
|
|
|
DefineFunc(lt => '%s < %s'); |
51
|
|
|
|
|
|
|
DefineFunc(gt => '%s > %s'); |
52
|
|
|
|
|
|
|
DefineFunc(le => '%s <= %s'); |
53
|
|
|
|
|
|
|
DefineFunc(ge => '%s >= %s'); |
54
|
|
|
|
|
|
|
DefineFunc(like => '%s LIKE %s'); |
55
|
|
|
|
|
|
|
DefineFunc(not_like => '%s NOT LIKE %s'); |
56
|
|
|
|
|
|
|
DefineFunc(date_eq => [$INT, '%s = DATE_ADD(NOW(), INTERVAL %s)']); |
57
|
|
|
|
|
|
|
DefineFunc(date_ne => [$INT, '%s != DATE_ADD(NOW(), INTERVAL %s)']); |
58
|
|
|
|
|
|
|
DefineFunc(date_lt => [$INT, '%s < DATE_ADD(NOW(), INTERVAL %s)']); |
59
|
|
|
|
|
|
|
DefineFunc(date_gt => [$INT, '%s > DATE_ADD(NOW(), INTERVAL %s)']); |
60
|
|
|
|
|
|
|
DefineFunc(date_le => [$INT, '%s <= DATE_ADD(NOW(), INTERVAL %s)']); |
61
|
|
|
|
|
|
|
DefineFunc(date_ge => [$INT, '%s >= DATE_ADD(NOW(), INTERVAL %s)']); |
62
|
|
|
|
|
|
|
DefineFunc(set_date => sub { |
63
|
|
|
|
|
|
|
my ($dbh, $f, $v) = @_; |
64
|
|
|
|
|
|
|
if (uc $v eq 'NOW') { |
65
|
|
|
|
|
|
|
return sprintf '%s = NOW()', $f; |
66
|
|
|
|
|
|
|
} elsif ($v =~ /$INT/mso) { |
67
|
|
|
|
|
|
|
return sprintf '%s = DATE_ADD(NOW(), INTERVAL %s)', $f, $dbh->quote($v), |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
return; |
70
|
|
|
|
|
|
|
}); |
71
|
|
|
|
|
|
|
DefineFunc(set_add => sub { |
72
|
|
|
|
|
|
|
my ($dbh, $f, $v) = @_; |
73
|
|
|
|
|
|
|
return sprintf '%s = %s + %s', $f, $f, $dbh->quote($v); |
74
|
|
|
|
|
|
|
}); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub DefineFunc { |
78
|
336
|
|
|
336
|
1
|
574
|
my ($func, $cmd) = @_; |
79
|
336
|
50
|
33
|
|
|
2295
|
if (!$func || ref $func || $func !~ /\A[A-Za-z]\w*\z/ms) { |
|
|
|
33
|
|
|
|
|
80
|
0
|
|
|
|
|
0
|
croak "bad function name: $func"; |
81
|
|
|
|
|
|
|
} |
82
|
336
|
100
|
|
|
|
716
|
if (!ref $cmd) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
83
|
126
|
50
|
|
|
|
402
|
if (2 != (() = $cmd =~ /%s/msg)) { |
84
|
0
|
|
|
|
|
0
|
croak "bad function: $cmd"; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
} elsif (ref $cmd eq 'ARRAY') { |
87
|
126
|
50
|
33
|
|
|
108
|
if (2 != @{$cmd} |
|
126
|
|
33
|
|
|
1081
|
|
|
|
|
33
|
|
|
|
|
88
|
|
|
|
|
|
|
|| ref $cmd->[0] ne 'Regexp' |
89
|
|
|
|
|
|
|
|| (ref $cmd->[1] || 2 != (() = $cmd->[1] =~ /%s/msg))) { |
90
|
0
|
|
|
|
|
0
|
croak "bad function: [@$cmd]"; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} elsif (ref $cmd ne 'CODE') { |
93
|
0
|
|
|
|
|
0
|
croak 'bad function'; |
94
|
|
|
|
|
|
|
} |
95
|
336
|
|
|
|
|
645
|
$Func{$func} = $cmd; |
96
|
336
|
|
|
|
|
442
|
return; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub _ret { |
100
|
0
|
|
|
0
|
|
|
my $cb = shift; |
101
|
0
|
0
|
|
|
|
|
if ($cb) { |
102
|
0
|
|
|
|
|
|
return $cb->(@_); |
103
|
|
|
|
|
|
|
} else { |
104
|
0
|
0
|
|
|
|
|
return wantarray ? @_ : $_[0]; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub _ret1 { |
109
|
0
|
|
|
0
|
|
|
my ($cb, $ret, $h) = @_; |
110
|
0
|
0
|
|
|
|
|
if ($cb) { |
111
|
0
|
|
|
|
|
|
return $cb->($ret, $h); |
112
|
|
|
|
|
|
|
} else { |
113
|
0
|
|
|
|
|
|
return $ret; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub _retdo { |
118
|
0
|
|
|
0
|
|
|
my ($dbh, $sql, $cb) = @_; |
119
|
0
|
0
|
|
|
|
|
if (!$cb) { |
120
|
0
|
|
|
|
|
|
return $dbh->do($sql); |
121
|
|
|
|
|
|
|
} |
122
|
0
|
|
|
|
|
|
return $dbh->do($sql, undef, $cb); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Set cache to given HASHREF, if any. |
126
|
|
|
|
|
|
|
# Initialize cache, if needed. |
127
|
|
|
|
|
|
|
# Return current cache. |
128
|
|
|
|
|
|
|
sub DBI::db::SecureCGICache { |
129
|
0
|
|
|
0
|
|
|
my ($dbh, $cache) = @_; |
130
|
0
|
0
|
0
|
|
|
|
if ($cache && ref $cache eq 'HASH') { |
131
|
0
|
|
|
|
|
|
$dbh->{$PRIVATE} = $cache; |
132
|
|
|
|
|
|
|
} else { |
133
|
0
|
|
0
|
|
|
|
$dbh->{$PRIVATE} //= {}; |
134
|
|
|
|
|
|
|
} |
135
|
0
|
|
|
|
|
|
return $dbh->{$PRIVATE}; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Ensure $dbh->All("DESC $table") is cached. |
139
|
|
|
|
|
|
|
# Return cached $dbh->All("DESC $table"). |
140
|
|
|
|
|
|
|
# On error set $dbh->err and return nothing. |
141
|
|
|
|
|
|
|
sub DBI::db::ColumnInfo { |
142
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $cb) = @_; |
143
|
0
|
|
|
|
|
|
my $cache = $dbh->SecureCGICache(); |
144
|
0
|
0
|
|
|
|
|
if ($cache->{$table}) { |
145
|
0
|
|
|
|
|
|
return _ret($cb, $cache->{$table}); |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
0
|
0
|
|
|
|
|
if (!$cb) { |
149
|
0
|
|
|
|
|
|
my @desc = $dbh->All('DESC '.$dbh->quote_identifier($table)); |
150
|
0
|
|
|
|
|
|
return _set_column_info($dbh, $cache, $table, undef, @desc); |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
return $dbh->All('DESC '.$dbh->quote_identifier($table), sub { |
153
|
0
|
|
|
0
|
|
|
my @desc = @_; |
154
|
0
|
|
|
|
|
|
return _set_column_info($dbh, $cache, $table, $cb, @desc); |
155
|
0
|
|
|
|
|
|
}); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub _set_column_info { |
159
|
0
|
|
|
0
|
|
|
my ($dbh, $cache, $table, $cb, @desc) = @_; |
160
|
0
|
0
|
|
|
|
|
if (@desc) { |
161
|
0
|
|
|
|
|
|
my @pk = grep {$desc[$_]{Key} eq 'PRI'} 0 .. $#desc; |
|
0
|
|
|
|
|
|
|
162
|
0
|
0
|
0
|
|
|
|
if (1 != @pk || $pk[0] != 0) { |
163
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "first field must be primary key: $table\n", undef, 'ColumnInfo')); |
164
|
|
|
|
|
|
|
} |
165
|
0
|
|
|
|
|
|
$cache->{$table} = \@desc; |
166
|
|
|
|
|
|
|
} |
167
|
0
|
|
|
|
|
|
return _ret($cb, $cache->{$table}); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# Ensure DESC for all $tables cached. |
171
|
|
|
|
|
|
|
# Return $dbh->SecureCGICache(). |
172
|
|
|
|
|
|
|
# On error set $dbh->err and return nothing. |
173
|
|
|
|
|
|
|
sub DBI::db::TableInfo { |
174
|
0
|
|
|
0
|
|
|
my ($dbh, $tables, $cb) = @_; |
175
|
0
|
0
|
|
|
|
|
my @tables = ref $tables eq 'ARRAY' ? @{$tables} : ($tables); |
|
0
|
|
|
|
|
|
|
176
|
0
|
0
|
0
|
|
|
|
if (!@tables || grep {/\A\z|\s/ms} @tables) { |
|
0
|
|
|
|
|
|
|
177
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "bad tables: [@tables]\n", undef, 'TableInfo')); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
0
|
0
|
|
|
|
|
if (!$cb) { |
181
|
0
|
|
|
|
|
|
while (@tables) { |
182
|
0
|
|
|
|
|
|
my $desc = $dbh->ColumnInfo(shift @tables); |
183
|
0
|
0
|
|
|
|
|
if (!$desc) { |
184
|
0
|
|
|
|
|
|
return; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
} |
187
|
0
|
|
|
|
|
|
return $dbh->SecureCGICache(); |
188
|
|
|
|
|
|
|
} |
189
|
0
|
|
|
|
|
|
my $code; $code = sub { |
190
|
0
|
|
|
0
|
|
|
my ($desc) = @_; |
191
|
0
|
0
|
|
|
|
|
if (!$desc) { |
192
|
0
|
|
|
|
|
|
undef $code; |
193
|
0
|
|
|
|
|
|
return $cb->(); |
194
|
|
|
|
|
|
|
} |
195
|
0
|
0
|
|
|
|
|
if (@tables) { |
196
|
0
|
|
|
|
|
|
return $dbh->ColumnInfo(shift @tables, $code); |
197
|
|
|
|
|
|
|
} |
198
|
0
|
|
|
|
|
|
undef $code; |
199
|
0
|
|
|
|
|
|
return $cb->( $dbh->SecureCGICache() ); |
200
|
0
|
|
|
|
|
|
}; |
201
|
0
|
|
|
|
|
|
return $dbh->ColumnInfo(shift @tables, $code); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub DBI::db::GetSQL { |
205
|
0
|
|
|
0
|
|
|
my ($dbh, $tables, $P, $cb) = @_; |
206
|
|
|
|
|
|
|
# remove possible JOIN info from table names for TableInfo() |
207
|
0
|
0
|
|
|
|
|
my @tables = map {my $s=$_;$s=~s/\s.*//ms;$s} ref $tables ? @{$tables} : $tables; ## no critic |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
208
|
0
|
0
|
|
|
|
|
if (!$cb) { |
209
|
0
|
|
|
|
|
|
my $cache = $dbh->TableInfo(\@tables); |
210
|
0
|
|
|
|
|
|
return _get_sql($dbh, $cache, $tables, $P); |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
return $dbh->TableInfo(\@tables, sub { |
213
|
0
|
|
|
0
|
|
|
my $cache = shift; |
214
|
0
|
|
|
|
|
|
return _get_sql($dbh, $cache, $tables, $P, $cb); |
215
|
0
|
|
|
|
|
|
}); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub _get_sql { ## no critic (ProhibitExcessComplexity) |
219
|
0
|
|
|
0
|
|
|
my ($dbh, $cache, $tables, $P, $cb) = @_; |
220
|
0
|
0
|
|
|
|
|
if (!$cache) { |
221
|
0
|
|
|
|
|
|
return _ret($cb); |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# Extract JOIN type info from table names |
225
|
0
|
|
|
|
|
|
my (@tables, @jointype); |
226
|
0
|
0
|
|
|
|
|
for (ref $tables eq 'ARRAY' ? @{$tables} : $tables) { |
|
0
|
|
|
|
|
|
|
227
|
0
|
0
|
|
|
|
|
if (!/\A(\S+)(?:\s+(LEFT|INNER))?\s*\z/msi) { |
228
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "unknown join type: $_\n", undef, 'GetSQL')); |
229
|
|
|
|
|
|
|
} |
230
|
0
|
|
|
|
|
|
push @tables, $1; |
231
|
0
|
|
0
|
|
|
|
push @jointype, $2 // 'INNER'; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
my %SQL = ( |
235
|
|
|
|
|
|
|
Table => $tables[0], |
236
|
|
|
|
|
|
|
ID => $cache->{ $tables[0] }[0]{Field}, |
237
|
|
|
|
|
|
|
Select => q{}, |
238
|
|
|
|
|
|
|
From => q{}, |
239
|
|
|
|
|
|
|
Set => q{}, |
240
|
|
|
|
|
|
|
Where => q{}, |
241
|
|
|
|
|
|
|
UpdateWhere => q{}, |
242
|
|
|
|
|
|
|
Order => q{}, |
243
|
|
|
|
|
|
|
Group => q{}, |
244
|
|
|
|
|
|
|
Limit => q{}, |
245
|
|
|
|
|
|
|
SelectLimit => q{}, |
246
|
|
|
|
|
|
|
); |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Detect keys which should be used for JOINing tables |
249
|
0
|
|
|
|
|
|
$SQL{From} = $dbh->quote_identifier($tables[0]); |
250
|
0
|
|
|
|
|
|
my @field = map {{ map {$_->{Field}=>1} @{ $cache->{$_} } }} @tables; ## no critic |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
TABLE: |
252
|
0
|
|
|
|
|
|
for my $right (1..$#tables) { |
253
|
|
|
|
|
|
|
## no critic (ProhibitAmbiguousNames) |
254
|
0
|
|
|
|
|
|
my $rkey = $cache->{ $tables[$right] }[0]{Field}; |
255
|
0
|
|
|
|
|
|
for my $left (0..$right-1) { |
256
|
0
|
|
|
|
|
|
my $lkey = $cache->{ $tables[$left] }[0]{Field}; |
257
|
0
|
0
|
|
|
|
|
my $key = $field[$left]{$rkey} ? $rkey : |
|
|
0
|
|
|
|
|
|
258
|
|
|
|
|
|
|
$field[$right]{$lkey} ? $lkey : next; |
259
|
0
|
|
|
|
|
|
$SQL{From} .= sprintf ' %s JOIN %s ON (%s.%s = %s.%s)', |
260
|
|
|
|
|
|
|
$jointype[$right], |
261
|
0
|
|
|
|
|
|
map { $dbh->quote_identifier($_) } |
262
|
|
|
|
|
|
|
$tables[$right], $tables[$right], $key, $tables[$left], $key; |
263
|
0
|
|
|
|
|
|
next TABLE; |
264
|
|
|
|
|
|
|
} |
265
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "can't join table: $tables[$right]\n", undef, 'GetSQL')); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# Set $SQL{Select} using qualified field names and without duplicates |
269
|
0
|
|
|
|
|
|
my %qualify; |
270
|
0
|
|
|
|
|
|
for my $t (@tables) { |
271
|
0
|
|
|
|
|
|
for my $f (map {$_->{Field}} @{ $cache->{$t} }) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
272
|
0
|
0
|
|
|
|
|
next if $qualify{$f}; |
273
|
0
|
|
|
|
|
|
$qualify{$f} = sprintf '%s.%s', |
274
|
0
|
|
|
|
|
|
map { $dbh->quote_identifier($_) } $t, $f; |
275
|
0
|
|
|
|
|
|
$SQL{Select} .= ', '.$qualify{$f}; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
0
|
|
|
|
|
|
$SQL{Select} =~ s/\A, //ms; |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Set $SQL{Set}, $SQL{Where}, $SQL{UpdateWhere} |
281
|
0
|
|
|
|
|
|
for my $k (keys %{$P}) { |
|
0
|
|
|
|
|
|
|
282
|
0
|
0
|
|
|
|
|
$k =~ /\A$IDENT(?:__(?!_)$IDENT)?\z/ms or next; # ignore non-field keys |
283
|
0
|
0
|
|
|
|
|
my $f = $qualify{$1} or next; # ignore non-field keys |
284
|
0
|
|
0
|
|
|
|
my $func= $2 // q{}; |
285
|
0
|
|
0
|
|
|
|
my $cmd = $Func{$func || 'eq'}; |
286
|
0
|
0
|
|
|
|
|
if (!$cmd) { |
287
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "unknown function: $k\n", undef, 'GetSQL')); |
288
|
|
|
|
|
|
|
} |
289
|
0
|
0
|
0
|
|
|
|
if (!$func && ref $P->{$k}) { |
290
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "ARRAYREF without function: $k\n", undef, 'GetSQL')); |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
# WARNING functions `eq' and `ne' must process value array themselves: |
293
|
0
|
|
0
|
|
|
|
my $is_list = ref $P->{$k} && $func ne 'eq' && $func ne 'ne'; |
294
|
0
|
0
|
|
|
|
|
for my $v ($is_list ? @{$P->{$k}} : $P->{$k}) { |
|
0
|
|
|
|
|
|
|
295
|
0
|
0
|
0
|
|
|
|
my $expr |
|
|
0
|
|
|
|
|
|
296
|
|
|
|
|
|
|
= ref $cmd eq 'CODE' ? $cmd->($dbh, $f, $v) |
297
|
|
|
|
|
|
|
: ref $cmd eq 'ARRAY' ? ($v =~ /$cmd->[0]/ms && sprintf $cmd->[1], $f, $v) |
298
|
|
|
|
|
|
|
: sprintf $cmd, $f, $dbh->quote($v); |
299
|
0
|
0
|
|
|
|
|
if (!$expr) { |
300
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "bad value for $k: $v\n", undef, 'GetSQL')); |
301
|
|
|
|
|
|
|
} |
302
|
0
|
0
|
0
|
|
|
|
$SQL{Set} .= ", $expr" if !$func || $func =~ /\Aset_/ms; |
303
|
0
|
0
|
|
|
|
|
$SQL{Where} .= " AND $expr" if $func !~ /\Aset_/ms; |
304
|
0
|
0
|
0
|
|
|
|
$SQL{UpdateWhere} .= " AND $expr" if $func && $func !~ /\Aset_/ms; |
305
|
0
|
0
|
|
|
|
|
$SQL{UpdateWhere} .= " AND $expr" if $k eq $SQL{ID}; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
0
|
|
|
|
|
|
$SQL{Set} =~ s/\A, //ms; |
309
|
0
|
|
|
|
|
|
$SQL{Where} =~ s/\A AND //ms; |
310
|
0
|
|
|
|
|
|
$SQL{UpdateWhere} =~ s/\A AND //ms; |
311
|
0
|
|
|
|
|
|
$SQL{Set} =~ s/\s+IS\s+NULL/ = NULL/msg; |
312
|
0
|
|
0
|
|
|
|
$SQL{Where} ||= '1'; |
313
|
0
|
|
0
|
|
|
|
$SQL{UpdateWhere} ||= '1'; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# Set $SQL{Order} and $SQL{Group} |
316
|
0
|
0
|
|
|
|
|
for my $order (ref $P->{__order} ? @{$P->{__order}} : $P->{__order}) { |
|
0
|
|
|
|
|
|
|
317
|
0
|
0
|
|
|
|
|
next if !defined $order; |
318
|
0
|
0
|
0
|
|
|
|
if ($order !~ /\A(\w+)\s*( ASC| DESC|)\z/ms || !$qualify{$1}) { |
319
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "bad __order value: $order\n", undef, 'GetSQL')); |
320
|
|
|
|
|
|
|
} |
321
|
0
|
|
|
|
|
|
$SQL{Order} .= ", $qualify{$1}$2"; |
322
|
|
|
|
|
|
|
} |
323
|
0
|
0
|
|
|
|
|
for my $group (ref $P->{__group} ? @{$P->{__group}} : $P->{__group}) { |
|
0
|
|
|
|
|
|
|
324
|
0
|
0
|
|
|
|
|
next if !defined $group; |
325
|
0
|
0
|
0
|
|
|
|
if ($group !~ /\A(\w+)\s*( ASC| DESC|)\z/ms || !$qualify{$1}) { |
326
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "bad __group value: $group\n", undef, 'GetSQL')); |
327
|
|
|
|
|
|
|
} |
328
|
0
|
|
|
|
|
|
$SQL{Group} .= ", $qualify{$1}$2"; |
329
|
|
|
|
|
|
|
} |
330
|
0
|
|
|
|
|
|
$SQL{Order} =~ s/\A, //ms; |
331
|
0
|
|
|
|
|
|
$SQL{Group} =~ s/\A, //ms; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# Set $SQL{Limit}, $SQL{SelectLimit} |
334
|
0
|
0
|
0
|
|
|
|
my @limit = ref $P->{__limit} ? @{$P->{__limit}} : $P->{__limit} // (); |
|
0
|
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
for (grep {!m/\A\d+\z/ms} @limit) { |
|
0
|
|
|
|
|
|
|
336
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "bad __limit value: $_\n", undef, 'GetSQL')); |
337
|
|
|
|
|
|
|
} |
338
|
0
|
0
|
|
|
|
|
if (@limit == 1) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
339
|
0
|
|
|
|
|
|
$SQL{Limit} = " $limit[0]"; # make __limit=>0 true value |
340
|
0
|
|
|
|
|
|
$SQL{SelectLimit} = " $limit[0]"; # make __limit=>0 true value |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
elsif (@limit == 2) { |
343
|
0
|
|
|
|
|
|
$SQL{SelectLimit} = join q{,}, @limit; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
elsif (@limit > 2) { |
346
|
0
|
|
|
|
|
|
return _ret($cb, $dbh->set_err($DBI::stderr, "too many __limit values: [@limit]\n", undef, 'GetSQL')); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
0
|
|
|
|
|
|
return _ret($cb, \%SQL); |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub DBI::db::Insert { |
353
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
354
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($table, $P) or return _ret1($cb, undef, $dbh); |
355
|
|
|
|
|
|
|
|
356
|
0
|
|
|
|
|
|
my $sql = sprintf 'INSERT INTO %s SET %s', |
357
|
|
|
|
|
|
|
$dbh->quote_identifier($SQL->{Table}), $SQL->{Set}; |
358
|
|
|
|
|
|
|
|
359
|
0
|
0
|
|
|
|
|
if (!$cb) { |
360
|
0
|
0
|
|
|
|
|
return $dbh->do($sql) ? $dbh->{mysql_insertid} : undef; |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
return $dbh->do($sql, undef, sub { |
363
|
0
|
|
|
0
|
|
|
my ($rv, $dbh) = @_; ## no critic (ProhibitReusedNames) |
364
|
0
|
0
|
|
|
|
|
return $cb->(($rv ? $dbh->{mysql_insertid} : undef), $dbh); |
365
|
0
|
|
|
|
|
|
}); |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
sub DBI::db::InsertIgnore { |
369
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
370
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($table, $P) or return _ret1($cb, undef, $dbh); |
371
|
|
|
|
|
|
|
|
372
|
0
|
|
|
|
|
|
my $sql = sprintf 'INSERT IGNORE INTO %s SET %s', |
373
|
|
|
|
|
|
|
$dbh->quote_identifier($SQL->{Table}), $SQL->{Set}; |
374
|
0
|
|
|
|
|
|
return _retdo($dbh, $sql, $cb); |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub DBI::db::Update { |
378
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
379
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($table, $P) or return _ret1($cb, undef, $dbh); |
380
|
0
|
0
|
0
|
|
|
|
if ($SQL->{UpdateWhere} eq '1' && !$P->{__force}) { |
381
|
0
|
|
|
|
|
|
return _ret1($cb, $dbh->set_err($DBI::stderr, "empty WHERE require {__force=>1}\n", undef, 'Update'), $dbh); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
0
|
0
|
0
|
|
|
|
my $sql = sprintf 'UPDATE %s SET %s WHERE %s' . ($SQL->{Limit} ? ' LIMIT %s' : q{}), |
385
|
|
|
|
|
|
|
$dbh->quote_identifier($SQL->{Table}), $SQL->{Set}, $SQL->{UpdateWhere}, |
386
|
|
|
|
|
|
|
$SQL->{Limit} || (); |
387
|
0
|
|
|
|
|
|
return _retdo($dbh, $sql, $cb); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub DBI::db::Replace { |
391
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
392
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($table, $P) or return _ret1($cb, undef, $dbh); |
393
|
|
|
|
|
|
|
|
394
|
0
|
|
|
|
|
|
my $sql = sprintf 'REPLACE INTO %s SET %s', |
395
|
|
|
|
|
|
|
$dbh->quote_identifier($SQL->{Table}), $SQL->{Set}; |
396
|
0
|
|
|
|
|
|
return _retdo($dbh, $sql, $cb); |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub _find_tables_for_delete { |
400
|
0
|
|
|
0
|
|
|
my ($dbh, $fields, $tables, $P, $cb) = @_; |
401
|
0
|
0
|
|
|
|
|
if (!@{$tables}) { |
|
0
|
|
|
|
|
|
|
402
|
0
|
|
|
|
|
|
return _ret1($cb, undef, $dbh); |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
0
|
|
|
|
|
|
my $found = []; |
406
|
0
|
0
|
|
|
|
|
if (!$cb) { |
407
|
0
|
|
|
|
|
|
for my $t (@{$tables}) { |
|
0
|
|
|
|
|
|
|
408
|
0
|
|
|
|
|
|
my $desc = $dbh->ColumnInfo($t); |
409
|
0
|
0
|
|
|
|
|
if ($desc) { |
410
|
0
|
|
|
|
|
|
my @columns = map {$_->{Field}} @{$desc}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
411
|
0
|
|
|
|
|
|
my %seen; |
412
|
0
|
0
|
|
|
|
|
if (@{$fields} == grep {++$seen{$_}==2} @{$fields}, @columns) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
413
|
0
|
|
|
|
|
|
push @{$found}, $t; |
|
0
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
} |
417
|
0
|
|
|
|
|
|
return $dbh->Delete($found, $P); |
418
|
|
|
|
|
|
|
} |
419
|
0
|
|
|
|
|
|
my $code; $code = sub { |
420
|
0
|
|
|
0
|
|
|
my ($desc) = @_; |
421
|
0
|
|
|
|
|
|
my $t = shift @{$tables}; |
|
0
|
|
|
|
|
|
|
422
|
0
|
0
|
|
|
|
|
if ($desc) { |
423
|
0
|
|
|
|
|
|
my @columns = map {$_->{Field}} @{$desc}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
424
|
0
|
|
|
|
|
|
my %seen; |
425
|
0
|
0
|
|
|
|
|
if (@{$fields} == grep {++$seen{$_}==2} @{$fields}, @columns) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
426
|
0
|
|
|
|
|
|
push @{$found}, $t; |
|
0
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
} |
429
|
0
|
0
|
|
|
|
|
if (@{$tables}) { |
|
0
|
|
|
|
|
|
|
430
|
0
|
|
|
|
|
|
return $dbh->ColumnInfo($tables->[0], $code); |
431
|
|
|
|
|
|
|
} |
432
|
0
|
|
|
|
|
|
undef $code; |
433
|
0
|
|
|
|
|
|
return $dbh->Delete($found, $P, $cb); |
434
|
0
|
|
|
|
|
|
}; |
435
|
0
|
|
|
|
|
|
return $dbh->ColumnInfo($tables->[0], $code); |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub DBI::db::Delete { ## no critic (ProhibitExcessComplexity) |
439
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
440
|
|
|
|
|
|
|
|
441
|
0
|
0
|
|
|
|
|
if (!defined $table) { |
442
|
0
|
0
|
|
|
|
|
my %fields = map {/\A$IDENT(?:__(?!_)$IDENT)?\z/ms ? ($1=>1) : ()} keys %{$P}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
443
|
0
|
|
|
|
|
|
my @fields = keys %fields; |
444
|
0
|
0
|
|
|
|
|
if (!@fields) { |
445
|
0
|
|
|
|
|
|
return _ret1($cb, $dbh->set_err($DBI::stderr, "table undefined, require params\n", undef, 'Delete'), $dbh); |
446
|
|
|
|
|
|
|
} |
447
|
0
|
0
|
|
|
|
|
if (!$cb) { |
448
|
0
|
|
|
|
|
|
return _find_tables_for_delete($dbh, \@fields, [$dbh->Col('SHOW TABLES')], $P); |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
return $dbh->Col('SHOW TABLES', sub { |
451
|
0
|
|
|
0
|
|
|
my (@tables) = @_; |
452
|
0
|
|
|
|
|
|
return _find_tables_for_delete($dbh, \@fields, \@tables, $P, $cb); |
453
|
0
|
|
|
|
|
|
}); |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
0
|
0
|
|
|
|
|
my @tables = ref $table ? @{$table} : $table; |
|
0
|
|
|
|
|
|
|
457
|
0
|
0
|
|
|
|
|
if (!$cb) { |
458
|
0
|
|
|
|
|
|
my $res; |
459
|
0
|
|
|
|
|
|
for my $t (@tables) { |
460
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($t, $P) or return; |
461
|
0
|
0
|
0
|
|
|
|
if ($SQL->{Where} eq '1' && !$P->{__force}) { |
462
|
0
|
|
|
|
|
|
return $dbh->set_err($DBI::stderr, "empty WHERE require {__force=>1}\n", undef, 'Delete'); |
463
|
|
|
|
|
|
|
} |
464
|
0
|
0
|
0
|
|
|
|
my $sql = sprintf 'DELETE FROM %s WHERE %s' . ($SQL->{Limit} ? ' LIMIT %s' : q{}), |
465
|
|
|
|
|
|
|
$dbh->quote_identifier($SQL->{Table}), $SQL->{Where}, $SQL->{Limit} || (); |
466
|
0
|
0
|
|
|
|
|
$res = $dbh->do($sql) or return; |
467
|
|
|
|
|
|
|
} |
468
|
0
|
|
|
|
|
|
return $res; |
469
|
|
|
|
|
|
|
} |
470
|
0
|
|
|
|
|
|
my $code; $code = sub { |
471
|
0
|
|
|
0
|
|
|
my ($SQL) = @_; |
472
|
0
|
|
|
|
|
|
my $t = shift @tables; |
473
|
0
|
0
|
|
|
|
|
if (!$SQL) { |
474
|
0
|
|
|
|
|
|
undef $code; |
475
|
0
|
|
|
|
|
|
return $cb->(undef, $dbh); |
476
|
|
|
|
|
|
|
} |
477
|
0
|
0
|
0
|
|
|
|
if ($SQL->{Where} eq '1' && !$P->{__force}) { |
478
|
0
|
|
|
|
|
|
undef $code; |
479
|
0
|
|
|
|
|
|
return $cb->($dbh->set_err($DBI::stderr, "empty WHERE require {__force=>1}\n", undef, 'Delete'), $dbh); |
480
|
|
|
|
|
|
|
} |
481
|
0
|
0
|
0
|
|
|
|
my $sql = sprintf 'DELETE FROM %s WHERE %s' . ($SQL->{Limit} ? ' LIMIT %s' : q{}), |
482
|
|
|
|
|
|
|
$dbh->quote_identifier($SQL->{Table}), $SQL->{Where}, $SQL->{Limit} || (); |
483
|
|
|
|
|
|
|
$dbh->do($sql, sub { |
484
|
0
|
|
|
|
|
|
my ($res, $dbh) = @_; ## no critic (ProhibitReusedNames) |
485
|
0
|
0
|
0
|
|
|
|
if ($res && @tables) { |
486
|
0
|
|
|
|
|
|
return $dbh->GetSQL($tables[0], $P, $code); |
487
|
|
|
|
|
|
|
} |
488
|
0
|
|
|
|
|
|
undef $code; |
489
|
0
|
|
|
|
|
|
return $cb->($res, $dbh); |
490
|
0
|
|
|
|
|
|
}); |
491
|
0
|
|
|
|
|
|
}; |
492
|
0
|
|
|
|
|
|
return $dbh->GetSQL($tables[0], $P, $code); |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
sub DBI::db::ID { |
496
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
497
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($table, $P) or return _ret1($cb, undef, $dbh); |
498
|
|
|
|
|
|
|
|
499
|
0
|
|
|
|
|
|
my $sql = sprintf 'SELECT %s.%s FROM %s WHERE %s' |
500
|
|
|
|
|
|
|
. ($SQL->{Order} ? ' ORDER BY %s' : q{}) |
501
|
|
|
|
|
|
|
. ($SQL->{SelectLimit} ? ' LIMIT %s' : q{}), |
502
|
0
|
0
|
0
|
|
|
|
(map { $dbh->quote_identifier($_) } $SQL->{Table}, $SQL->{ID}), |
|
|
0
|
0
|
|
|
|
|
503
|
|
|
|
|
|
|
$SQL->{From}, $SQL->{Where}, $SQL->{Order} || (), $SQL->{SelectLimit} || (); |
504
|
0
|
|
0
|
|
|
|
return $dbh->Col($sql, $cb // ()); |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
sub DBI::db::Count { |
508
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
509
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($table, $P) or return _ret1($cb, undef, $dbh); |
510
|
|
|
|
|
|
|
|
511
|
0
|
|
|
|
|
|
my $sql = sprintf 'SELECT count(*) __count FROM %s WHERE %s', |
512
|
|
|
|
|
|
|
$SQL->{From}, $SQL->{Where}; |
513
|
0
|
|
0
|
|
|
|
return $dbh->Col($sql, $cb // ()); |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
sub DBI::db::Select { |
517
|
0
|
|
|
0
|
|
|
my ($dbh, $table, $P, $cb) = @_; |
518
|
0
|
0
|
|
|
|
|
my $SQL = $dbh->GetSQL($table, $P) or return _ret1($cb, undef, $dbh); |
519
|
|
|
|
|
|
|
|
520
|
0
|
0
|
0
|
|
|
|
my $sql = sprintf 'SELECT %s' |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
521
|
|
|
|
|
|
|
. ($SQL->{Group} ? ', count(*) __count' : q{}) |
522
|
|
|
|
|
|
|
. ' FROM %s WHERE %s' |
523
|
|
|
|
|
|
|
. ($SQL->{Group} ? ' GROUP BY %s' : q{}) |
524
|
|
|
|
|
|
|
. ($SQL->{Order} ? ' ORDER BY %s' : q{}) |
525
|
|
|
|
|
|
|
. ($SQL->{SelectLimit} ? ' LIMIT %s' : q{}), |
526
|
|
|
|
|
|
|
$SQL->{Select}, $SQL->{From}, $SQL->{Where}, |
527
|
|
|
|
|
|
|
$SQL->{Group} || (), $SQL->{Order} || (), $SQL->{SelectLimit} || (); |
528
|
0
|
0
|
|
|
|
|
if (!$cb) { |
529
|
0
|
0
|
|
|
|
|
return wantarray ? $dbh->All($sql) : $dbh->Row($sql); |
530
|
|
|
|
|
|
|
} |
531
|
0
|
|
|
|
|
|
return $dbh->All($sql, $cb); |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
sub _is_cb { |
535
|
0
|
|
|
0
|
|
|
my $cb = shift; |
536
|
0
|
|
|
|
|
|
my $ref = ref $cb; |
537
|
0
|
|
0
|
|
|
|
return $ref eq 'CODE' || $ref eq 'AnyEvent::CondVar'; |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
sub DBI::db::All { |
541
|
0
|
|
|
0
|
|
|
my ($dbh, $sql, @bind) = @_; |
542
|
0
|
0
|
0
|
|
|
|
my $cb = @bind && _is_cb($bind[-1]) ? pop @bind : undef; |
543
|
0
|
0
|
|
|
|
|
if (!$cb) { |
544
|
0
|
0
|
|
|
|
|
(my $sth = $dbh->prepare($sql, {async=>0}))->execute(@bind) or return; |
545
|
0
|
|
|
|
|
|
return @{ $sth->fetchall_arrayref({}) }; |
|
0
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
return $dbh->prepare($sql)->execute(@bind, sub { |
548
|
0
|
|
|
0
|
|
|
my ($rv, $sth) = @_; |
549
|
0
|
0
|
|
|
|
|
return $cb->(!$rv ? () : @{ $sth->fetchall_arrayref({}) }); |
|
0
|
|
|
|
|
|
|
550
|
0
|
|
|
|
|
|
}); |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
sub DBI::db::Row { |
554
|
0
|
|
|
0
|
|
|
my ($dbh, $sql, @bind) = @_; |
555
|
0
|
|
|
|
|
|
return $dbh->selectrow_hashref($sql, undef, @bind); |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
sub DBI::db::Col { |
559
|
0
|
|
|
0
|
|
|
my ($dbh, $sql, @bind) = @_; |
560
|
0
|
0
|
0
|
|
|
|
my $cb = @bind && _is_cb($bind[-1]) ? pop @bind : undef; |
561
|
0
|
0
|
|
|
|
|
if (!$cb) { |
562
|
0
|
0
|
|
|
|
|
my @res = @{ $dbh->selectcol_arrayref($sql, undef, @bind) || [] }; |
|
0
|
|
|
|
|
|
|
563
|
0
|
0
|
|
|
|
|
return wantarray ? @res : $res[0]; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
return $dbh->selectcol_arrayref($sql, undef, @bind, sub { |
566
|
0
|
|
|
0
|
|
|
my ($ary_ref) = @_; |
567
|
0
|
0
|
|
|
|
|
return $cb->($ary_ref ? @{ $ary_ref } : ()); |
|
0
|
|
|
|
|
|
|
568
|
0
|
|
|
|
|
|
}); |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
573
|
|
|
|
|
|
|
__END__ |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=encoding utf8 |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=head1 NAME |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
DBIx::SecureCGI - Secure conversion of CGI params hash to SQL |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=head1 SYNOPSIS |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
#--- sync |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
use DBIx::SecureCGI; |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
$row = $dbh->Select('Table', \%Q); |
589
|
|
|
|
|
|
|
@rows = $dbh->Select(['Table1','Table2'], {%Q, id_user=>$id}); |
590
|
|
|
|
|
|
|
$count = $dbh->Count('Table', {age__gt=>25}); |
591
|
|
|
|
|
|
|
$id = $dbh->ID('Table', {login=>$login, pass=>$pass}); |
592
|
|
|
|
|
|
|
@id = $dbh->ID('Table', {age__gt=>25}); |
593
|
|
|
|
|
|
|
$newid = $dbh->Insert('Table', \%Q); |
594
|
|
|
|
|
|
|
$rv = $dbh->InsertIgnore('Table', \%Q); |
595
|
|
|
|
|
|
|
$rv = $dbh->Update('Table', \%Q); |
596
|
|
|
|
|
|
|
$rv = $dbh->Replace('Table', \%Q); |
597
|
|
|
|
|
|
|
$rv = $dbh->Delete('Table', \%Q); |
598
|
|
|
|
|
|
|
$rv = $dbh->Delete(undef, {id_user=>$id}); |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
@rows = $dbh->All('SELECT * FROM Table WHERE id_user=?', $id); |
601
|
|
|
|
|
|
|
$row = $dbh->Row('SELECT * FROM Table WHERE id_user=?', $id); |
602
|
|
|
|
|
|
|
@col = $dbh->Col('SELECT id_user FROM Table'); |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
$SQL = $dbh->GetSQL(['Table1','Table2'], \%Q); |
605
|
|
|
|
|
|
|
$cache = $dbh->TableInfo(['Table1','Table2']); |
606
|
|
|
|
|
|
|
$desc = $dbh->ColumnInfo('Table'); |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
#--- async |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
use AnyEvent::DBI::MySQL; |
612
|
|
|
|
|
|
|
use DBIx::SecureCGI; |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
$dbh->Select(â¦, sub { my (@rows) = @_; ⦠}); |
615
|
|
|
|
|
|
|
$dbh->Count(â¦, sub { my ($count) = @_; ⦠}); |
616
|
|
|
|
|
|
|
$dbh->ID(â¦, sub { my (@id) = @_; ⦠}); |
617
|
|
|
|
|
|
|
$dbh->Insert(â¦, sub { my ($newid, $dbh) = @_; ⦠}); |
618
|
|
|
|
|
|
|
$dbh->InsertIgnore(â¦, sub { my ($rv, $dbh) = @_; ⦠}); |
619
|
|
|
|
|
|
|
$dbh->Update(â¦, sub { my ($rv, $dbh) = @_; ⦠}); |
620
|
|
|
|
|
|
|
$dbh->Replace(â¦, sub { my ($rv, $dbh) = @_; ⦠}); |
621
|
|
|
|
|
|
|
$dbh->Delete(â¦, sub { my ($rv, $dbh) = @_; ⦠}); |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
$dbh->All(â¦, sub { my (@rows) = @_; ⦠}); |
624
|
|
|
|
|
|
|
$dbh->Row(â¦, sub { my ($row) = @_; ⦠}); |
625
|
|
|
|
|
|
|
$dbh->Col(â¦, sub { my (@col) = @_; ⦠}); |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
$dbh->GetSQL(â¦, sub { my ($SQL) = @_; ⦠}); |
628
|
|
|
|
|
|
|
$dbh->TableInfo(â¦, sub { my ($cache) = @_; ⦠}); |
629
|
|
|
|
|
|
|
$dbh->ColumnInfo(â¦, sub { my ($desc) = @_; ⦠}); |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
#--- setup |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
DBIx::SecureCGI::DefineFunc( $name, '%s op %s' ) |
635
|
|
|
|
|
|
|
DBIx::SecureCGI::DefineFunc( $name, [ qr/regexp/, '%s op %s' ] ) |
636
|
|
|
|
|
|
|
DBIx::SecureCGI::DefineFunc( $name, sub { ⦠} ) |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
$cache = $dbh->SecureCGICache(); |
639
|
|
|
|
|
|
|
$dbh->SecureCGICache($new_cache); |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
=head1 DESCRIPTION |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
This module let you use B<hash with CGI params> to make (or just generate) |
645
|
|
|
|
|
|
|
SQL queries to MySQL database in B<easy and secure> way. To make this |
646
|
|
|
|
|
|
|
magic possible there are some limitations and requirements: |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=over |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=item * Your app and db scheme must conform to these L</"CONVENTIONS"> |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=item * Small speed penalty/extra queries to load scheme from db |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=item * No support for advanced SQL, only basic queries |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
=back |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
Example: if all CGI params (including unrelated to db table 'Table') are |
659
|
|
|
|
|
|
|
in C<%Q>, then: |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
@rows = $dbh->Select('Table', \%Q); |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
will execute any simple C<SELECT> query from the table C<Table> (defined |
664
|
|
|
|
|
|
|
by user-supplied parameters in C<%Q>); and this: |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
@user_rows = $dbh->Select('Table', {%Q, id_user=>$id}); |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
will make any similar query limited to records with C<id_user> column |
669
|
|
|
|
|
|
|
value C<$id> (thus allowing user to fetch any or B<his own> records). |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
The module is intended for use only with a fairly simple tables and simple |
672
|
|
|
|
|
|
|
SQL queries. More advanced queries usually can be generated manually with |
673
|
|
|
|
|
|
|
help of L</GetSQL> or you can just use plain L<DBI> methods. |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
Also it support B<non-blocking SQL queries> using L<AnyEvent::DBI::MySQL> |
676
|
|
|
|
|
|
|
and thus can be effectively used with event-based CGI frameworks like |
677
|
|
|
|
|
|
|
L<Mojolicious> or with event-based FastCGI servers like L<FCGI::EV>. |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Finally, it can be used in non-CGI environment, as simplified interface to |
680
|
|
|
|
|
|
|
L<DBI>. |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=head2 SECURITY OVERVIEW |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
At a glance, generating SQL queries based on untrusted parameters sent by |
685
|
|
|
|
|
|
|
user to your CGI looks very unsafe. But interface of this module designed |
686
|
|
|
|
|
|
|
to make it safe - while you conform to some L</CONVENTIONS> and follow |
687
|
|
|
|
|
|
|
some simple guidelines. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=over |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=item * B<User have no control over query type (SELECT/INSERT/â¦)> |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
It's defined by method name you call. |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=item * B<User have no control over tables involved in SQL query> |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
It's defined by separate (first) parameter in all methods, unrelated to |
698
|
|
|
|
|
|
|
hash with CGI parameters. |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=item * B<User have no direct control over SQL query> |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
All values from hash are either quoted before inserting into SQL, or |
703
|
|
|
|
|
|
|
checked using very strict regular expressions if it's impossible to quote |
704
|
|
|
|
|
|
|
them (like for date/time C<INTERVAL> values). |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=item * B<You can block/control access to "secure" fields in all tables> |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
Name all such fields in some special way (like beginning with "C<_>") and |
709
|
|
|
|
|
|
|
when receiving CGI parameters immediately B<delete all keys> in hash which |
710
|
|
|
|
|
|
|
match these fields (i.e. all keys beginning with "C<_>"). Later you can |
711
|
|
|
|
|
|
|
analyse user's request and manually add to hash keys for these fields |
712
|
|
|
|
|
|
|
before call method to execute SQL query. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=item * B<You can limit user's access to some subset of records> |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
Just instead of using plain C<\%Q> as parameter for methods use |
717
|
|
|
|
|
|
|
something like C<< { %Q, id_user => $id } >> - this way user will be |
718
|
|
|
|
|
|
|
limited to records with C<$id> value in C<id_user> column. |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=back |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
Within these security limitations user can do anything - select records |
723
|
|
|
|
|
|
|
with custom C<WHERE>, C<GROUP BY>, C<ORDER BY>, C<LIMIT>; set any values |
724
|
|
|
|
|
|
|
(allowed by table scheme, of course) for any fields on C<INSERT> or |
725
|
|
|
|
|
|
|
C<UPDATE>; etc. without any single line of your code - exclusively by |
726
|
|
|
|
|
|
|
using different CGI parameters. |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=head1 HOW IT WORKS |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
Each CGI parameter belongs to one of three categories: |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=over |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
=item * B<related to some table's field in db:> C<fieldname>, |
736
|
|
|
|
|
|
|
C<fieldname__funcname> |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=item * B<control command:> C<__commandname> |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=item * B<your app's parameter> |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=back |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
It's recommended to name fields in db beginning with B<lowercase> letter |
745
|
|
|
|
|
|
|
or B<underscore>, and name your app's parameters beginning with |
746
|
|
|
|
|
|
|
B<Uppercase> letter to avoid occasional clash with field name. |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
To protect some fields (like "C<balance>" or "C<privileges>") from |
749
|
|
|
|
|
|
|
uncontrolled access you can use simple convention: name these fields in db |
750
|
|
|
|
|
|
|
beginning with "C<_>"; when receiving CGI params just |
751
|
|
|
|
|
|
|
B<delete all with names beginning with> "C<_>" - thus it won't be possible |
752
|
|
|
|
|
|
|
to access these fields from CGI params. This module doesn't know about |
753
|
|
|
|
|
|
|
these protected fields and handle them just as usual fields. So, you |
754
|
|
|
|
|
|
|
should later add needed keys for these fields into hash before calling |
755
|
|
|
|
|
|
|
methods to execute SQL query. This way all operations on these fields will |
756
|
|
|
|
|
|
|
be controlled by your app. |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
You can use any other similar naming scheme which won't conflict with |
759
|
|
|
|
|
|
|
L</CONVENTIONS> below - DBIx::SecureCGI will analyse db scheme (and |
760
|
|
|
|
|
|
|
cache it for speed) to detect which keys match field names. |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
CGI params may have several values. In hash, keys for such params must |
763
|
|
|
|
|
|
|
have C<ARRAYREF> value. DBIx::SecureCGI support this only for keys which |
764
|
|
|
|
|
|
|
contain "C<__>" (double underscore). Depending on used CGI framework you |
765
|
|
|
|
|
|
|
may need to convert existing CGI parameters into this format. |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
Error handling: all unknown keys will be silently ignored, all other |
768
|
|
|
|
|
|
|
errors (unable to detect key for joining table, field without |
769
|
|
|
|
|
|
|
"C<__funcname>" have C<ARRAYREF> value, unknown "C<__funcname>" function, etc.) |
770
|
|
|
|
|
|
|
will return usual DBI errors (or throw exceptions when C<< {RaiseError=>1} >>. |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=head2 CONVENTIONS |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=over |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
=item * |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
Each table's B<first field> must be a C<PRIMARY KEY>. |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=over |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
MOTIVATION: This module use simplified analyse of db scheme and suppose |
783
|
|
|
|
|
|
|
first field in every table is a C<PRIMARY KEY>. To add support for complex |
784
|
|
|
|
|
|
|
primary keys or tables without primary keys we should first define how |
785
|
|
|
|
|
|
|
L</ID> should handle them and how to automatically join such tables. |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
=back |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=item * |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
Two tables are always C<JOIN>ed using field which must be C<PRIMARY KEY> |
792
|
|
|
|
|
|
|
at least in one of them and have B<same name in both tables>. |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=over |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
So, don't name your primary key "C<id>" if you plan to join this table with |
797
|
|
|
|
|
|
|
another - name it like "C<id_thistable>" or "C<thistableId>". |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=back |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
If both tables have field corresponding to C<PRIMARY KEY> in other table, |
802
|
|
|
|
|
|
|
then key field of B<right table> (in order defined when you make array of |
803
|
|
|
|
|
|
|
tables in first param of method) will be used. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
If more than two tables C<JOIN>ed, then each table starting from second |
806
|
|
|
|
|
|
|
one will try to join to each of the previous tables (starting at first |
807
|
|
|
|
|
|
|
table) until it find table with suitable field. If it wasn't found |
808
|
|
|
|
|
|
|
DBI error will be returned. |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=over |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
MOTIVATION: Let this module automatically join tables. |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
=back |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=item * |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
Field names must not contain "C<__>" (two adjoined underscore). |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=over |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
MOTIVATION: Distinguish special commands for this module from field names. |
823
|
|
|
|
|
|
|
Also, some methods sometimes create aliases for fields and their names |
824
|
|
|
|
|
|
|
begins with "C<__>". |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
=back |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=item * |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
Hash with CGI params may contain several values (as C<ARRAYREF>) only for key |
831
|
|
|
|
|
|
|
names containing "C<__>" (keys unrelated to fields may have any values). |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=over |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
MOTIVATION: Allowing C<< { field => \@values } >> introduce many |
836
|
|
|
|
|
|
|
ambiguities and in fact same as C<< { field__eq => \@values } >>, |
837
|
|
|
|
|
|
|
so it's safer to deny it. |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
=back |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
=back |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
=head2 Hash to SQL convertion rules |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
=head3 __commandname |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
Keys beginning with "C<__>" are control commands. Supported commands are: |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
=over |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=item B<__order> |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
Define value for C<ORDER BY>. Valid values are: |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
'field_name' |
856
|
|
|
|
|
|
|
'field_name ASC' |
857
|
|
|
|
|
|
|
'field_name DESC' |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
Multiple values can be given as C<ARRAYREF>. |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
=item B<__group> |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
Define value for C<GROUP BY>. Valid values are same as for B<__order>. |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=item B<__limit> |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
Can have up to two numeric values (when it's C<ARRAYREF>), set C<LIMIT>. |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=item B<__force> |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
If the value of B<__force> key is true, then it's allowed to run |
872
|
|
|
|
|
|
|
L</Update> and L</Delete> with an empty C<WHERE>. (This isn't a security |
873
|
|
|
|
|
|
|
feature, it's just for convenience to protect against occasional damage on |
874
|
|
|
|
|
|
|
database while playing with CGI parameters.) |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
=back |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
Examples: |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
my @rows = $dbh->Select('Table', { |
881
|
|
|
|
|
|
|
age__ge => 20, |
882
|
|
|
|
|
|
|
age__lt => 30, |
883
|
|
|
|
|
|
|
__group => 'age', |
884
|
|
|
|
|
|
|
__order => ['age DESC', 'fname'], |
885
|
|
|
|
|
|
|
__limit => 5, |
886
|
|
|
|
|
|
|
}); |
887
|
|
|
|
|
|
|
$dbh->Delete('Table', { __force => 1 }); |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
=head3 fieldname__funcname |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
If the key contains a "C<__>" then it is treated as applying function |
892
|
|
|
|
|
|
|
"C<funcname>" to field "C<fieldname>". |
893
|
|
|
|
|
|
|
If the there is no field with such name in database, this key is ignored. |
894
|
|
|
|
|
|
|
A valid key value - string/number or a reference to an array of |
895
|
|
|
|
|
|
|
strings/numbers. |
896
|
|
|
|
|
|
|
A list of available functions in this version is shown below. |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
Unless special behavior mentioned functions handle C<ARRAYREF> value by |
899
|
|
|
|
|
|
|
applying itself to each value in array and joining with C<AND>. |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
Example: |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
{ html__like => ['%<P>%', '%<BR>%'] } |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
will be transformed in SQL to |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
html LIKE '%<P>%' AND html LIKE '%<BR>%' |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
Typically, such keys are used in C<WHERE>, except when "C<funcname>" begins |
910
|
|
|
|
|
|
|
with "C<set_>" - such keys will be used in C<SET>. |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
=head3 fieldname |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
Other keys are treated as names of fields in database. |
915
|
|
|
|
|
|
|
If there is no field with such name, then key is ignored. |
916
|
|
|
|
|
|
|
A valid value for these keys - scalar. |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
Example: |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
{ name => 'Alex' } |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
will be transformed in SQL to |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
name = 'Alex' |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
Typically, such keys are used in part C<SET>, except for C<PRIMARY KEY> |
927
|
|
|
|
|
|
|
field in L</Update> - it will be used in C<WHERE>. |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
=head1 INTERFACE |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
=head2 Functions |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=head3 DefineFunc |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
DBIx::SecureCGI::DefineFunc( $name, '%s op %s' ); |
937
|
|
|
|
|
|
|
DBIx::SecureCGI::DefineFunc( $name, [ qr/regexp/, '%s op %s' ] ); |
938
|
|
|
|
|
|
|
DBIx::SecureCGI::DefineFunc( $name, sub { ⦠} ); |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
Define new or replace existing function applied to fields after "C<__>" |
941
|
|
|
|
|
|
|
delimiter. |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
SQL expression for that function will be generated in different ways, |
944
|
|
|
|
|
|
|
depending on how you defined that function - using string, regexp+string |
945
|
|
|
|
|
|
|
or code: |
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
$expr = sprintf '%s op %s', $field, $dbh->quote($value); |
948
|
|
|
|
|
|
|
$expr = $value =~ /regexp/ && sprintf '%s op %s', $field, $value; |
949
|
|
|
|
|
|
|
$expr = $code->($dbh, $field, $value); |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
If C<$expr> will be false DBI error will be returned. |
952
|
|
|
|
|
|
|
Here is example of code implementation: |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
sub { |
955
|
|
|
|
|
|
|
my ($dbh, $f, $v) = @_; |
956
|
|
|
|
|
|
|
if (⦠value ok â¦) { |
957
|
|
|
|
|
|
|
return sprintf 'â¦', $f, $dbh->quote($v); |
958
|
|
|
|
|
|
|
} |
959
|
|
|
|
|
|
|
return; # wrong value |
960
|
|
|
|
|
|
|
} |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
=head2 Methods injected into DBI |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=head3 GetSQL |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
$SQL = $dbh->GetSQL( $table, \%Q ); |
968
|
|
|
|
|
|
|
$dbh->GetSQL( $table, \%Q, sub { my ($SQL) = @_; ⦠} ); |
969
|
|
|
|
|
|
|
$SQL = $dbh->GetSQL( \@tables, \%Q ); |
970
|
|
|
|
|
|
|
$dbh->GetSQL( \@tables, \%Q, sub { my ($SQL) = @_; ⦠} ); |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
This is helper function which will analyse (cached) database scheme for |
973
|
|
|
|
|
|
|
given tables and generate elements of SQL query for given keys in C<%Q>. |
974
|
|
|
|
|
|
|
You may use it to write own methods like L</Select> or L</Insert>. |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
In C<%Q> keys which doesn't match field names in C<$table> / C<@tables> |
977
|
|
|
|
|
|
|
are ignored. |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
Names of tables and fields in all keys (except C<{Table}> and C<{ID}>) |
980
|
|
|
|
|
|
|
are already quoted, field names qualified with table name (so they're |
981
|
|
|
|
|
|
|
ready for inserting into SQL query). Values of C<{Table}> and C<{ID}> |
982
|
|
|
|
|
|
|
should be escaped with C<< $dbh->quote_identifier() >> before using in SQL |
983
|
|
|
|
|
|
|
query. |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
Returns C<HASHREF> with keys: |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
{Table} first of the used tables |
988
|
|
|
|
|
|
|
{ID} name of PRIMARY KEY field in {Table} |
989
|
|
|
|
|
|
|
{Select} list of all field names which should be returned by |
990
|
|
|
|
|
|
|
'SELECT *' excluding duplicated fields (when field with |
991
|
|
|
|
|
|
|
same name exist in many tables only field from first table |
992
|
|
|
|
|
|
|
will be returned); field names in {Select} are joined with "," |
993
|
|
|
|
|
|
|
{From} all tables joined using chosen JOIN type (INNER by default) |
994
|
|
|
|
|
|
|
{Set} string like "field=value, field2=value2" for all simple |
995
|
|
|
|
|
|
|
"fieldname" keys in %Q |
996
|
|
|
|
|
|
|
{Where} a-la {Set}, except fields joined using "AND" and added |
997
|
|
|
|
|
|
|
"field__function" fields; if there are no fields it will |
998
|
|
|
|
|
|
|
be set to string "1" |
999
|
|
|
|
|
|
|
{UpdateWhere} a-la {Where}, except it uses only "field__function" keys |
1000
|
|
|
|
|
|
|
plus one PRIMARY KEY "fieldname" key (if it exists in %Q) |
1001
|
|
|
|
|
|
|
{Order} string like "field1 ASC, field2 DESC" or empty string |
1002
|
|
|
|
|
|
|
{Group} a-la {Order} |
1003
|
|
|
|
|
|
|
{Limit} set to value of __limit if it contain one number |
1004
|
|
|
|
|
|
|
{SelectLimit} set to value of __limit if it contain one number, |
1005
|
|
|
|
|
|
|
or to values of __limit joined with "," if it contain |
1006
|
|
|
|
|
|
|
two numbers |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
Example : |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
CREATE TABLE A ( |
1011
|
|
|
|
|
|
|
id_a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, |
1012
|
|
|
|
|
|
|
i INT NOT NULL |
1013
|
|
|
|
|
|
|
); |
1014
|
|
|
|
|
|
|
CREATE TABLE B ( |
1015
|
|
|
|
|
|
|
id_b INT NOT NULL AUTO_INCREMENT PRIMARY KEY, |
1016
|
|
|
|
|
|
|
id_a INT NOT NULL, |
1017
|
|
|
|
|
|
|
s VARCHAR(255) NOT NULL |
1018
|
|
|
|
|
|
|
); |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
$SQL = $dbh->GetSQL(['A', 'B LEFT'], { |
1021
|
|
|
|
|
|
|
id_a => 3, |
1022
|
|
|
|
|
|
|
i => 10, |
1023
|
|
|
|
|
|
|
s => 'str', |
1024
|
|
|
|
|
|
|
id_b__gt => 5, |
1025
|
|
|
|
|
|
|
__group => 'i', |
1026
|
|
|
|
|
|
|
__order => ['s DESC', 'i'], |
1027
|
|
|
|
|
|
|
__limit => [50,10], |
1028
|
|
|
|
|
|
|
}); |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
# now %$SQL have these values: |
1031
|
|
|
|
|
|
|
# (backticks added by $dbh->quote_identifier() around all table/field |
1032
|
|
|
|
|
|
|
# names omitted for readability) |
1033
|
|
|
|
|
|
|
Table => 'A' |
1034
|
|
|
|
|
|
|
ID => 'id_a' |
1035
|
|
|
|
|
|
|
Select => 'A.id_a, A.i, B.id_b, B.s' |
1036
|
|
|
|
|
|
|
From => 'A LEFT JOIN B ON (B.id_a = A.id_a)' |
1037
|
|
|
|
|
|
|
Set => 'B.s = "str", A.id_a = 3, A.i = 10' |
1038
|
|
|
|
|
|
|
Where => 'B.s = "str" AND A.id_a = 3 AND A.i = 10 AND B.id_b > 5' |
1039
|
|
|
|
|
|
|
UpdateWhere => ' A.id_a = 3 AND B.id_b > 5' |
1040
|
|
|
|
|
|
|
Group => 'A.i' |
1041
|
|
|
|
|
|
|
Order => 'B.s DESC, A.i' |
1042
|
|
|
|
|
|
|
Limit => '' |
1043
|
|
|
|
|
|
|
SelectLimit => '50,10' |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
=head3 Insert |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
$newid = $dbh->Insert( $table, \%Q ); |
1049
|
|
|
|
|
|
|
$dbh->Insert( $table, \%Q, sub { my ($newid, $dbh) = @_; ⦠} ); |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
Execute SQL query: |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
INSERT INTO {Table} SET {Set} |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
Return C<< $dbh->{mysql_insertid} >> on success or C<undef> on error. |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
It's B<strongly recommended> to always use |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
$dbh->Insert( â¦, { %Q, â¦, primary_key_name=>undef }, ⦠) |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
because if you didn't force C<primary_key> field to be C<NULL> in SQL (and |
1062
|
|
|
|
|
|
|
thus use C<AUTO_INCREMENT> value) then user may send CGI parameter to set |
1063
|
|
|
|
|
|
|
it to C<-1> or C<4294967295> and this will result in B<DoS> because no |
1064
|
|
|
|
|
|
|
more records can be added using C<AUTO_INCREMENT> into this table. |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
=head3 InsertIgnore |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
$rv = $dbh->InsertIgnore( $table, \%Q ); |
1070
|
|
|
|
|
|
|
$dbh->InsertIgnore( $table, \%Q, sub { my ($rv, $dbh) = @_; ⦠} ); |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
Execute SQL query: |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
INSERT IGNORE INTO {Table} SET {Set} |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
Return C<$rv> (true on success or C<undef> on error). |
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
=head3 Update |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
$rv = $dbh->Update( $table, \%Q ); |
1082
|
|
|
|
|
|
|
$dbh->Update( $table, \%Q, sub { my ($rv, $dbh) = @_; ⦠} ); |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
Execute SQL query: |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
UPDATE {Table} SET {Set} WHERE {UpdateWhere} [LIMIT {Limit}] |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
Uses in C<SET> part all fields given as "C<fieldname>", in C<WHERE> part all |
1089
|
|
|
|
|
|
|
fields given as "C<fieldname__funcname>" plus C<PRIMARY KEY> field if it was |
1090
|
|
|
|
|
|
|
given as "C<fieldname>". |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
Return C<$rv> (amount of modified records on success or C<undef> on error). |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
To use with empty C<WHERE> part require C<< {__force=>1} >> in C<%Q>. |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
=head3 Replace |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
$rv = $dbh->Replace( $table, \%Q ); |
1100
|
|
|
|
|
|
|
$dbh->Replace( $table, \%Q, sub { my ($rv, $dbh) = @_; ⦠} ); |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
Execute SQL query: |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
REPLACE INTO {Table} SET {Set} |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
Uses in C<SET> part all fields given as "C<fieldname>". |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
Return C<$rv> (true on success or C<undef> on error). |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
=head3 Delete |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
$rv = $dbh->Delete( $table, \%Q ); |
1114
|
|
|
|
|
|
|
$dbh->Delete( $table, \%Q, sub { my ($rv, $dbh) = @_; ⦠} ); |
1115
|
|
|
|
|
|
|
$rv = $dbh->Delete( \@tables, \%Q ); |
1116
|
|
|
|
|
|
|
$dbh->Delete( \@tables, \%Q, sub { my ($rv, $dbh) = @_; ⦠} ); |
1117
|
|
|
|
|
|
|
$rv = $dbh->Delete( undef, \%Q ); |
1118
|
|
|
|
|
|
|
$dbh->Delete( undef, \%Q, sub { my ($rv, $dbh) = @_; ⦠} ); |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
Execute SQL query: |
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
DELETE FROM {Table} WHERE {Where} [LIMIT {Limit}] |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
Delete records from C<$table> or (one-by-one) from each table in |
1125
|
|
|
|
|
|
|
C<@tables>. If C<undef> given, then delete records from B<ALL> tables |
1126
|
|
|
|
|
|
|
(except C<TEMPORARY>) which have B<ALL> fields mentioned in C<%Q>. |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
To use with empty C<WHERE> part require C<< {__force=>1} >> in C<%Q>. |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
Return C<$rv> (amount of deleted records or C<undef> on error). |
1131
|
|
|
|
|
|
|
If used to delete records from more than one table - return C<$rv> |
1132
|
|
|
|
|
|
|
for last table. If error happens it will be immediately returned, |
1133
|
|
|
|
|
|
|
so some tables may not be processed in this case. |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
=head3 ID |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
$id = $dbh->ID( $table, \%Q ); |
1139
|
|
|
|
|
|
|
@id = $dbh->ID( $table, \%Q ); |
1140
|
|
|
|
|
|
|
$dbh->ID( $table, \%Q, sub { my (@id) = @_; ⦠} ); |
1141
|
|
|
|
|
|
|
$id = $dbh->ID( \@tables, \%Q ); |
1142
|
|
|
|
|
|
|
@id = $dbh->ID( \@tables, \%Q ); |
1143
|
|
|
|
|
|
|
$dbh->ID( \@tables, \%Q, sub { my (@id) = @_; ⦠} ); |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
Return result of executing this SQL query using L</Col>: |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
SELECT {ID} FROM {From} WHERE {Where} |
1148
|
|
|
|
|
|
|
[ORDER BY {Order}] [LIMIT {SelectLimit}] |
1149
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=head3 Count |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
$count = $dbh->Count( $table, \%Q ); |
1154
|
|
|
|
|
|
|
$dbh->Count( $table, \%Q, sub { my ($count) = @_; ⦠} ); |
1155
|
|
|
|
|
|
|
$count = $dbh->Count( \@tables, \%Q ); |
1156
|
|
|
|
|
|
|
$dbh->Count( \@tables, \%Q, sub { my ($count) = @_; ⦠} ); |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
Return result of executing this SQL query using L</Col>: |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
SELECT count(*) __count FROM {From} WHERE {Where} |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
=head3 Select |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
$row = $dbh->Select( $table, \%Q ); |
1166
|
|
|
|
|
|
|
@rows = $dbh->Select( $table, \%Q ); |
1167
|
|
|
|
|
|
|
$dbh->Select( $table, \%Q, sub { my (@rows) = @_; ⦠} ); |
1168
|
|
|
|
|
|
|
$row = $dbh->Select( \@tables, \%Q ); |
1169
|
|
|
|
|
|
|
@rows = $dbh->Select( \@tables, \%Q ); |
1170
|
|
|
|
|
|
|
$dbh->Select( \@tables, \%Q, sub { my (@rows) = @_; ⦠} ); |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
Execute one of these SQL queries (depending on using C<__group> command): |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
SELECT * FROM {From} WHERE {Where} |
1175
|
|
|
|
|
|
|
[ORDER BY {Order}] [LIMIT {SelectLimit}] |
1176
|
|
|
|
|
|
|
SELECT *, count(*) __count FROM {From} WHERE {Where} GROUP BY {Group} |
1177
|
|
|
|
|
|
|
[ORDER BY {Order}] [LIMIT {SelectLimit}] |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
Instead of C<SELECT *> it uses enumeration of all fields qualified using |
1180
|
|
|
|
|
|
|
table name; if same field found in several tables it's included only |
1181
|
|
|
|
|
|
|
one - from first table having that field. |
1182
|
|
|
|
|
|
|
|
1183
|
|
|
|
|
|
|
In C<@tables> you can append C<' LEFT'> or C<' INNER'> to table name to |
1184
|
|
|
|
|
|
|
choose C<JOIN> variant (by default C<INNER JOIN> will be used): |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
$dbh->Select(['TableA', 'TableB LEFT', 'TableC'], â¦) |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
Return result of executing SQL query using L</All> when called in list |
1189
|
|
|
|
|
|
|
context or L</Row> when called in scalar context. |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
=head3 All |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
@rows = $dbh->All( $sql, @bind ) |
1195
|
|
|
|
|
|
|
$dbh->All( $sql, @bind, sub { my (@rows) = @_; ⦠} ); |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
Shortcut for this ugly but very useful snippet: |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
@{ $dbh->selectall_arrayref($sql, {Slice=>{}}, @bind) } |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
=head3 Row |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
$row = $dbh->Row( $sql, @bind ); |
1205
|
|
|
|
|
|
|
$dbh->Row( $sql, @bind, sub { my ($row) = @_; ⦠} ); |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
Shortcut for: |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
$dbh->selectrow_hashref($sql, undef, @bind) |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
If you wonder why it exists, the answer is simple: it was added circa |
1212
|
|
|
|
|
|
|
2002, when there was no C<< $dbh->selectrow_hashref() >> and now it |
1213
|
|
|
|
|
|
|
continue to exists for compatibility and to complement L</All> |
1214
|
|
|
|
|
|
|
and L</Col>. |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
=head3 Col |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
$col = $dbh->Col( $sql, @bind ); |
1220
|
|
|
|
|
|
|
@col = $dbh->Col( $sql, @bind ); |
1221
|
|
|
|
|
|
|
$dbh->Col( $sql, @bind, sub { my (@col) = @_; ⦠} ); |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
Shortcut for: |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
$col = $dbh->selectcol_arrayref($sql, undef, @bind)->[0]; |
1226
|
|
|
|
|
|
|
@col = @{ $dbh->selectcol_arrayref($sql, undef, @bind) }; |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
=head3 SecureCGICache |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
$cache = $dbh->SecureCGICache(); |
1232
|
|
|
|
|
|
|
$cache = $dbh->SecureCGICache( $new_cache ); |
1233
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
Fetch (or set when C<$new_cache> given) C<HASHREF> with cached results of |
1235
|
|
|
|
|
|
|
"C<DESC tablename>" SQL queries for all tables used previous in any methods. |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
You may need to reset cache (by using C<{}> as C<$new_cache> value) if |
1238
|
|
|
|
|
|
|
you've changed scheme for tables already accessed by any method or if you |
1239
|
|
|
|
|
|
|
changed current database. |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
Also in some environments when many different C<$dbh> used simultaneously, |
1242
|
|
|
|
|
|
|
connected to same database (like in event-based environments) it may make |
1243
|
|
|
|
|
|
|
sense to share same cache for all C<$dbh>. |
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
=head3 TableInfo |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
$cache = $dbh->TableInfo( $table ); |
1249
|
|
|
|
|
|
|
$dbh->TableInfo( $table, sub { my ($cache) = @_; ⦠} ); |
1250
|
|
|
|
|
|
|
$cache = $dbh->TableInfo( \@tables ); |
1251
|
|
|
|
|
|
|
$dbh->TableInfo( \@tables, sub { my ($cache) = @_; ⦠} ); |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
Ensure "C<DESC tablename>" for all C<$table> / C<@tables> is cached. |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
Return same as L</SecureCGICache> on success or C<undef> on error. |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
=head3 ColumnInfo |
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
$desc = $dbh->ColumnInfo( $table ); |
1261
|
|
|
|
|
|
|
$dbh->ColumnInfo( $table, sub { my ($desc) = @_; ⦠} ); |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
Ensure "C<DESC $table>" is cached. |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
Return result of C<< $dbh->All("DESC $table") >> on success or C<undef> on |
1266
|
|
|
|
|
|
|
error. |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
=head2 __funcname functions for fields |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
These functions can be added and replaced using L</DefineFunc>. |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
Functions which can be used in C<%Q> as "C<fieldname_funcname>": |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
=head3 eq, ne, lt, gt, le, ge |
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
field = value field IS NULL |
1278
|
|
|
|
|
|
|
field != value field IS NOT NULL |
1279
|
|
|
|
|
|
|
field < value |
1280
|
|
|
|
|
|
|
field > value |
1281
|
|
|
|
|
|
|
field <= value |
1282
|
|
|
|
|
|
|
field >= value |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
For functions B<eq> or B<ne>: |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
eq [] - NOT 1 |
1287
|
|
|
|
|
|
|
ne [] - NOT 0 |
1288
|
|
|
|
|
|
|
eq only undef - name IS NULL |
1289
|
|
|
|
|
|
|
ne only undef - name IS NOT NULL |
1290
|
|
|
|
|
|
|
eq without undef - name IN (...) |
1291
|
|
|
|
|
|
|
ne without undef - (name IS NULL OR name NOT IN (...)) |
1292
|
|
|
|
|
|
|
eq with undef - (name IS NULL OR name IN (...)) |
1293
|
|
|
|
|
|
|
ne with undef - name NOT IN (...) |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
where |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
"[]" : name__func=>[] |
1298
|
|
|
|
|
|
|
"only undef": name__func=>undef or name__func=>[undef] |
1299
|
|
|
|
|
|
|
"without undef": name__func=>$defined or name__func=>[@defined] |
1300
|
|
|
|
|
|
|
"with undef": name__func=>[@defined_and_not_defined] |
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
=head3 like, not_like |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
field LIKE value |
1305
|
|
|
|
|
|
|
field NOT LIKE value |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
=head3 date_eq, date_ne, date_lt, date_gt, date_le, date_ge |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
field = DATE_ADD(NOW(), INTERVAL value) |
1310
|
|
|
|
|
|
|
field != DATE_ADD(NOW(), INTERVAL value) |
1311
|
|
|
|
|
|
|
field < DATE_ADD(NOW(), INTERVAL value) |
1312
|
|
|
|
|
|
|
field > DATE_ADD(NOW(), INTERVAL value) |
1313
|
|
|
|
|
|
|
field <= DATE_ADD(NOW(), INTERVAL value) |
1314
|
|
|
|
|
|
|
field >= DATE_ADD(NOW(), INTERVAL value) |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
value must match: |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
/^-?\d+ (?:SECOND|MINUTE|HOUR|DAY|MONTH|YEAR)$/ |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
=head3 set_add |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
field = field + value |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
When used in L</Update> it will be in C<SET> instead of C<WHERE>. |
1325
|
|
|
|
|
|
|
It doesn't make sense to use this function with L</Insert>, |
1326
|
|
|
|
|
|
|
L</InsertIgnore> or L</Replace>. |
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
=head3 set_date |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
field = NOW() |
1331
|
|
|
|
|
|
|
field = DATE_ADD(NOW(), INTERVAL value) |
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
If it's value is (case-insensitive) string C<'NOW'> then it'll use |
1334
|
|
|
|
|
|
|
C<NOW()> else it will use C<DATE_ADD(â¦)>. |
1335
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
When used in L</Insert>, L</InsertIgnore>, L</Update> and L</Replace> it |
1337
|
|
|
|
|
|
|
will be in C<SET>. |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
1341
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
No bugs have been reported. |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
Only MySQL supported. |
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
It's impossible to change C<PRIMARY KEY> using L</Update> with: |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
{ id => $new_id, id__eq => $old_id } |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
because both "C<id>" and "C<id__eq>" will be in C<WHERE> part: |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
SET id = $new_id WHERE id = $new_id AND id = $old_id |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
and if we won't add C<< 'id => $new_id' >> in C<WHERE> part if we have |
1355
|
|
|
|
|
|
|
C< 'id__eq' >, then we'll have do use this |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
$dbh->Func($table, {%Q, id_user=>$S{id_user}, id_user__eq=>$S{id_user}) |
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
in B<all> CGI requests to protect against attempt to read someone else's |
1360
|
|
|
|
|
|
|
records or change own records's id_user field by using C<'id_user'> |
1361
|
|
|
|
|
|
|
or C<'id_user__eq'> CGI params. |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
=head1 SUPPORT |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at |
1367
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=DBIx-SecureCGI>. |
1368
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress |
1369
|
|
|
|
|
|
|
on your bug as I make changes. |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
You can also look for information at: |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
=over |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=DBIx-SecureCGI> |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
L<http://annocpan.org/dist/DBIx-SecureCGI> |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
=item * CPAN Ratings |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
L<http://cpanratings.perl.org/d/DBIx-SecureCGI> |
1386
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
=item * Search CPAN |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/DBIx-SecureCGI/> |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
=back |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
=head1 AUTHORS |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
Alex Efros C<< <powerman@cpan.org> >> |
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
Nikita Savin C<< <nikita@asdfGroup.com> >> |
1399
|
|
|
|
|
|
|
|
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
Copyright 2002-2014 Alex Efros <powerman@cpan.org>. |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
This program is distributed under the MIT (X11) License: |
1406
|
|
|
|
|
|
|
L<http://www.opensource.org/licenses/mit-license.php> |
1407
|
|
|
|
|
|
|
|
1408
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person |
1409
|
|
|
|
|
|
|
obtaining a copy of this software and associated documentation |
1410
|
|
|
|
|
|
|
files (the "Software"), to deal in the Software without |
1411
|
|
|
|
|
|
|
restriction, including without limitation the rights to use, |
1412
|
|
|
|
|
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell |
1413
|
|
|
|
|
|
|
copies of the Software, and to permit persons to whom the |
1414
|
|
|
|
|
|
|
Software is furnished to do so, subject to the following |
1415
|
|
|
|
|
|
|
conditions: |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be |
1418
|
|
|
|
|
|
|
included in all copies or substantial portions of the Software. |
1419
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
1421
|
|
|
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
1422
|
|
|
|
|
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
1423
|
|
|
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
1424
|
|
|
|
|
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
1425
|
|
|
|
|
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
1426
|
|
|
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
1427
|
|
|
|
|
|
|
OTHER DEALINGS IN THE SOFTWARE. |
1428
|
|
|
|
|
|
|
|