line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# vim: set ts=2 sts=2 sw=2 expandtab smarttab: |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# This file is part of DBIx-RoboQuery |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# This software is copyright (c) 2010 by Randy Stauner. |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# This is free software; you can redistribute it and/or modify it under |
8
|
|
|
|
|
|
|
# the same terms as the Perl 5 programming language system itself. |
9
|
|
|
|
|
|
|
# |
10
|
8
|
|
|
8
|
|
37
|
use strict; |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
291
|
|
11
|
8
|
|
|
8
|
|
39
|
use warnings; |
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
418
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
package DBIx::RoboQuery::ResultSet; |
14
|
|
|
|
|
|
|
{ |
15
|
|
|
|
|
|
|
$DBIx::RoboQuery::ResultSet::VERSION = '0.032'; |
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
BEGIN { |
18
|
8
|
|
|
8
|
|
165
|
$DBIx::RoboQuery::ResultSet::AUTHORITY = 'cpan:RWSTAUNER'; |
19
|
|
|
|
|
|
|
} |
20
|
|
|
|
|
|
|
# ABSTRACT: Configure the results to get what you want |
21
|
|
|
|
|
|
|
|
22
|
8
|
|
|
8
|
|
43
|
use Carp qw(croak carp); |
|
8
|
|
|
|
|
10
|
|
|
8
|
|
|
|
|
460
|
|
23
|
8
|
|
|
8
|
|
6340
|
use Timer::Simple; |
|
8
|
|
|
|
|
15184
|
|
|
8
|
|
|
|
|
21450
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub new { |
27
|
34
|
|
|
34
|
1
|
4958
|
my $class = shift; |
28
|
34
|
|
|
|
|
52
|
my $query = shift; |
29
|
34
|
100
|
|
|
|
134
|
my %opts = ref($_[0]) eq 'HASH' ? %{$_[0]} : @_; |
|
12
|
|
|
|
|
54
|
|
30
|
34
|
|
|
|
|
179
|
my $self = { |
31
|
|
|
|
|
|
|
row_count => -1, |
32
|
|
|
|
|
|
|
times => {}, |
33
|
|
|
|
|
|
|
query => $query, |
34
|
|
|
|
|
|
|
default_slice => {}, |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Process the template in case it changes anything (like query.key_columns) |
37
|
|
|
|
|
|
|
# so that everything will get passed to the ResultSet. |
38
|
|
|
|
|
|
|
sql => $query->sql(), |
39
|
|
|
|
|
|
|
}; |
40
|
|
|
|
|
|
|
|
41
|
34
|
|
|
|
|
115
|
bless $self, $class; |
42
|
|
|
|
|
|
|
|
43
|
34
|
|
|
|
|
118
|
foreach my $var ( $self->_pass_through_args() ){ |
44
|
|
|
|
|
|
|
# allow options to be specified directly |
45
|
306
|
100
|
|
|
|
848
|
if( exists($opts{$var}) ){ |
|
|
100
|
|
|
|
|
|
46
|
36
|
|
|
|
|
144
|
$self->{$var} = $opts{$var}; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
# or look for them on the query object |
49
|
|
|
|
|
|
|
elsif( exists($query->{$var}) ){ |
50
|
74
|
|
|
|
|
198
|
$self->{$var} = $query->{$var}; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
34
|
|
|
|
|
146
|
DBIx::RoboQuery::Util::_ensure_arrayrefs($self); |
55
|
|
|
|
|
|
|
|
56
|
34
|
|
50
|
|
|
379
|
$self->{hash_key_name} ||= |
|
|
|
33
|
|
|
|
|
57
|
|
|
|
|
|
|
($self->{dbh} && $self->{dbh}{FetchHashKeyName}) |
58
|
|
|
|
|
|
|
|| 'NAME_lc'; |
59
|
|
|
|
|
|
|
|
60
|
34
|
|
|
|
|
172
|
return $self; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub array { |
65
|
2
|
|
|
2
|
1
|
31
|
my ($self, @args) = @_; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# default to an array of hashrefs if no arguments are given |
68
|
2
|
100
|
|
|
|
9
|
@args = $self->{default_slice} |
69
|
|
|
|
|
|
|
unless @args; |
70
|
|
|
|
|
|
|
|
71
|
2
|
50
|
|
|
|
14
|
$self->execute() if !$self->{executed}; |
72
|
|
|
|
|
|
|
|
73
|
2
|
50
|
|
|
|
9
|
croak('Columns unknown. Was this a SELECT?') |
74
|
|
|
|
|
|
|
unless $self->{all_columns}; |
75
|
|
|
|
|
|
|
|
76
|
2
|
|
|
|
|
5
|
my @tr_args = (); |
77
|
2
|
50
|
|
|
|
16
|
if( @args ){ |
78
|
|
|
|
|
|
|
# if the slice is empty, fill it with the non-drop_columns |
79
|
2
|
|
|
|
|
4
|
my $slice = $args[0]; |
80
|
2
|
100
|
66
|
|
|
28
|
if( ref($slice) eq 'HASH' and !keys(%$slice) ){ |
|
|
50
|
33
|
|
|
|
|
81
|
1
|
|
|
|
|
5
|
$slice->{$_} = 1 for $self->columns; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
elsif( ref($slice) eq 'ARRAY' and !@$slice ){ |
84
|
1
|
|
|
|
|
2
|
my @col = @{$self->{all_columns}}; |
|
1
|
|
|
|
|
4
|
|
85
|
1
|
|
|
|
|
3
|
my %drop = map { $_ => 1 } @{ $self->{drop_columns} }; |
|
0
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
4
|
|
86
|
|
|
|
|
|
|
# turn [] into a list of (possibly non-contiguous) indexes: [1,2,4,5] |
87
|
1
|
|
|
|
|
4
|
push(@$slice, grep { !$drop{ $col[$_] } } 0 .. $#col); |
|
3
|
|
|
|
|
8
|
|
88
|
|
|
|
|
|
|
# set the first (only) element to an arrayref of column names |
89
|
1
|
|
|
|
|
6
|
@tr_args = ( [@col[@$slice]] ); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
2
|
|
|
|
|
6
|
my $tr = $self->{transformations}; |
94
|
2
|
|
|
|
|
8
|
my $t = Timer::Simple->new; |
95
|
|
|
|
|
|
|
|
96
|
2
|
|
|
|
|
22
|
my $rows = $self->{sth}->fetchall_arrayref(@args); |
97
|
2
|
50
|
|
|
|
124
|
$rows = [map { $tr->call(@tr_args, $_) } @$rows] |
|
6
|
|
|
|
|
38342
|
|
98
|
|
|
|
|
|
|
if $tr; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# include transformations in the time for consistency with hash() |
101
|
2
|
|
|
|
|
4994
|
$self->{times}{fetch} = $t->stop; |
102
|
2
|
|
|
|
|
216
|
$self->{row_count} = @$rows; |
103
|
|
|
|
|
|
|
|
104
|
2
|
|
|
|
|
18
|
return $rows; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# convenience method for subclasses |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub _arrayref_args { |
110
|
68
|
|
|
68
|
|
94
|
my ($self) = @_; |
111
|
68
|
|
|
|
|
243
|
return $self->{query}->_arrayref_args; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
115
|
0
|
|
|
0
|
1
|
0
|
sub bound_params { $_[0]->query->bound_params } |
116
|
0
|
|
|
0
|
1
|
0
|
sub bound_values { $_[0]->query->bound_values } |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub columns { |
120
|
7
|
|
|
7
|
1
|
13
|
my ($self) = @_; |
121
|
7
|
50
|
|
|
|
30
|
croak('Columns not known until after the statement has executed') |
122
|
|
|
|
|
|
|
unless $self->{executed}; |
123
|
7
|
|
|
|
|
8
|
return @{ $self->{columns} }; |
|
7
|
|
|
|
|
40
|
|
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub drop_columns { |
128
|
7
|
|
|
7
|
1
|
2161
|
return @{$_[0]->{drop_columns}}; |
|
7
|
|
|
|
|
36
|
|
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub execute { |
133
|
16
|
|
|
16
|
1
|
169
|
my ($self, @params) = @_; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# the sql attribute is cached from $query->sql in the constructor |
136
|
16
|
|
|
|
|
30
|
my $sql = $self->{sql}; |
137
|
|
|
|
|
|
|
|
138
|
16
|
|
|
|
|
111
|
my $t = Timer::Simple->new; |
139
|
16
|
50
|
|
|
|
2944
|
my $sth = $self->{sth} = $self->{dbh}->prepare($sql) |
140
|
|
|
|
|
|
|
or croak $self->{dbh}->errstr; |
141
|
16
|
|
|
|
|
1096
|
$self->{times}{prepare} = $t->stop; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# call bind_param() regardless of @params b/c bind_param can specify a type |
144
|
16
|
100
|
|
|
|
867
|
if( my $bind = $self->{bind_params} ){ |
145
|
1
|
|
|
|
|
2
|
local $_; |
146
|
1
|
|
|
|
|
9
|
$sth->bind_param(@$_) for @$bind; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
16
|
|
|
|
|
361
|
$t->restart; |
150
|
16
|
50
|
|
|
|
796
|
$self->{executed} = $sth->execute(@params) |
151
|
|
|
|
|
|
|
or croak $sth->errstr; |
152
|
16
|
|
|
|
|
877
|
$self->{times}{execute} = $t->stop; |
153
|
|
|
|
|
|
|
|
154
|
16
|
100
|
|
|
|
788
|
if( my $columns = $sth->{ $self->{hash_key_name} } ){ |
155
|
|
|
|
|
|
|
# save the full order for later (but break the reference) |
156
|
15
|
|
|
|
|
47
|
$self->{all_columns} = [@$columns]; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# cache groups of column names |
159
|
15
|
|
|
|
|
61
|
foreach my $set ( |
160
|
|
|
|
|
|
|
# preserve the order of non-dropped columns |
161
|
|
|
|
|
|
|
[qw( columns all_columns drop_columns )], |
162
|
|
|
|
|
|
|
# make the list of non-key columns available separately |
163
|
|
|
|
|
|
|
[qw( non_key_columns columns key_columns )], |
164
|
|
|
|
|
|
|
){ |
165
|
30
|
|
|
|
|
51
|
my ($make, $from, $minus) = @$set; |
166
|
30
|
|
|
|
|
33
|
my %other = map { ($_ => 1) } @{ $self->{ $minus } }; |
|
29
|
|
|
|
|
81
|
|
|
30
|
|
|
|
|
63
|
|
167
|
30
|
|
|
|
|
45
|
$self->{ $make } = [ grep { !$other{$_} } @{ $self->{ $from } } ]; |
|
120
|
|
|
|
|
310
|
|
|
30
|
|
|
|
|
54
|
|
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
15
|
50
|
|
|
|
63
|
if( my $transformations = $self->{transformations} ){ |
171
|
15
|
|
|
|
|
113
|
foreach my $groups ( |
172
|
|
|
|
|
|
|
[key => $self->{key_columns}], |
173
|
|
|
|
|
|
|
[non_key => $self->{non_key_columns}], |
174
|
|
|
|
|
|
|
# aliases |
175
|
|
|
|
|
|
|
[key_columns => {in => 'key'}], |
176
|
|
|
|
|
|
|
[non_key_columns => {in => 'non_key'}], |
177
|
|
|
|
|
|
|
){ |
178
|
60
|
|
|
|
|
3061
|
$transformations->group(@$groups); |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
# set all the columns so we can use group exclusions |
181
|
15
|
|
|
|
|
832
|
$transformations->fields(@$columns); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# FIXME: check $sth->errstr (or someting: see DBI) |
186
|
|
|
|
|
|
|
# to make sure we got all records without error |
187
|
|
|
|
|
|
|
|
188
|
16
|
|
|
|
|
598
|
return $self->{executed}; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub hash { |
193
|
6
|
|
|
6
|
1
|
725
|
my ($self) = @_; |
194
|
6
|
100
|
|
|
|
48
|
$self->execute() if !$self->{executed}; |
195
|
|
|
|
|
|
|
# TODO: care if this is called more than once? |
196
|
6
|
|
|
|
|
13
|
my $sth = $self->{sth}; |
197
|
|
|
|
|
|
|
|
198
|
6
|
100
|
|
|
|
10
|
my @key_columns = @{ $self->{key_columns} } |
|
6
|
|
|
|
|
52
|
|
199
|
|
|
|
|
|
|
or croak('Cannot use hash() with an empty key_columns attribute'); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# We could just return $sth->fetchall_hashref(\@key_columns) if there are |
202
|
|
|
|
|
|
|
# no preferences but we can't slice out the dropped columns that way. |
203
|
|
|
|
|
|
|
|
204
|
5
|
|
|
|
|
8
|
my @drop_columns = @{ $self->{drop_columns} }; |
|
5
|
|
|
|
|
11
|
|
205
|
5
|
|
|
|
|
9
|
my @columns = (@key_columns, @{ $self->{non_key_columns} }); |
|
5
|
|
|
|
|
13
|
|
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# we have to save the dropped columns so we can send them to preference() |
208
|
5
|
|
|
|
|
10
|
my ($root, $dropped) = ({}, {}); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# NOTE: It seemed to me more powerful to transform the data upon fetch |
211
|
|
|
|
|
|
|
# rather than upon storage in the tree: it gives you the option of |
212
|
|
|
|
|
|
|
# pre-transforming the keys to adjust the way the tree is built |
213
|
|
|
|
|
|
|
# and lets you know what to expect in the preference rules. |
214
|
|
|
|
|
|
|
# Plus it was easier to implement. |
215
|
|
|
|
|
|
|
# I can't think of a reason to want transform the key columns in the record |
216
|
|
|
|
|
|
|
# but not the tree (ex: {A => {B => {k1 => 'a', k2 => 'b'}}}) |
217
|
|
|
|
|
|
|
# If you want un-adultered data for preferences you can select the column |
218
|
|
|
|
|
|
|
# again with an alias and then drop it. |
219
|
|
|
|
|
|
|
|
220
|
5
|
|
|
|
|
8
|
my $tr = $self->{transformations}; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# we only increase the row count for new (not overridden) hashes |
223
|
5
|
|
|
|
|
8
|
my $count = 0; |
224
|
5
|
|
|
|
|
44
|
my $t = Timer::Simple->new; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# check for preferences once... if there are none, do the quick version |
227
|
5
|
100
|
66
|
|
|
33
|
if( !$self->{preferences} || !@{$self->{preferences}} ){ |
228
|
|
|
|
|
|
|
# we can't honor drop_columns with fetchall_hashref(), so fake it |
229
|
3
|
|
|
|
|
21
|
while( my $row = $sth->fetchrow_hashref() ){ |
230
|
15
|
50
|
|
|
|
1217
|
$row = $tr->call($row) if $tr; |
231
|
15
|
|
|
|
|
29648
|
my $hash = $root; |
232
|
15
|
|
100
|
|
|
227
|
$hash = ($hash->{ $row->{$_} } ||= {}) for @key_columns; |
233
|
15
|
100
|
|
|
|
66
|
++$count unless keys %$hash; |
234
|
15
|
|
|
|
|
179
|
@$hash{@columns} = @$row{@columns}; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
else { |
238
|
2
|
|
|
|
|
13
|
while( my $row = $sth->fetchrow_hashref() ){ |
239
|
12
|
50
|
|
|
|
655
|
$row = $tr->call($row) if $tr; |
240
|
12
|
|
|
|
|
956
|
my ($hash, $drop) = ($root, $dropped); |
241
|
|
|
|
|
|
|
# traverse hash tree to get to {key1 => {key2 => {record}}} |
242
|
12
|
|
|
|
|
23
|
foreach ( @key_columns ){ |
243
|
24
|
|
100
|
|
|
83
|
$hash = ($hash->{ $row->{$_} } ||= {}); |
244
|
24
|
|
100
|
|
|
93
|
$drop = ($drop->{ $row->{$_} } ||= {}); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
# if there's already a record there (not an empty hash) |
247
|
|
|
|
|
|
|
# (a few benchmarks suggest keys() may be faster than exists()) |
248
|
12
|
100
|
|
|
|
31
|
if( keys %$hash ){ |
249
|
4
|
|
|
|
|
23
|
$row = $self->preference({%$drop, %$hash}, $row); |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
else { |
252
|
8
|
|
|
|
|
9
|
++$count; |
253
|
|
|
|
|
|
|
} |
254
|
12
|
|
|
|
|
39
|
@$drop{@drop_columns} = @$row{@drop_columns}; |
255
|
12
|
|
|
|
|
116
|
@$hash{@columns} = @$row{@columns}; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
} |
258
|
5
|
|
|
|
|
498
|
$self->{times}{fetch} = $t->stop; |
259
|
5
|
|
|
|
|
343
|
$self->{row_count} = $count; |
260
|
5
|
|
|
|
|
41
|
return $root; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub key_columns { |
265
|
8
|
|
|
8
|
1
|
1766
|
my ($self) = @_; |
266
|
8
|
|
|
|
|
10
|
return @{$self->{key_columns}}; |
|
8
|
|
|
|
|
45
|
|
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub non_key_columns { |
271
|
4
|
|
|
4
|
1
|
8
|
my ($self) = @_; |
272
|
4
|
50
|
|
|
|
15
|
croak('Columns not known until after the statement has executed') |
273
|
|
|
|
|
|
|
unless $self->{executed}; |
274
|
|
|
|
|
|
|
# An empty array should mean that the rest are key or drop columns. |
275
|
|
|
|
|
|
|
# If not defined, there's a problem. |
276
|
4
|
50
|
|
|
|
10
|
croak('Columns unknown. Was this a SELECT?') |
277
|
|
|
|
|
|
|
unless $self->{non_key_columns}; |
278
|
4
|
|
|
|
|
5
|
return @{$self->{non_key_columns}}; |
|
4
|
|
|
|
|
26
|
|
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# convenience method: args allowed in the constructor |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
sub _pass_through_args { |
284
|
|
|
|
|
|
|
( |
285
|
34
|
|
|
34
|
|
164
|
$_[0]->_arrayref_args, |
286
|
|
|
|
|
|
|
qw( |
287
|
|
|
|
|
|
|
dbh |
288
|
|
|
|
|
|
|
default_slice |
289
|
|
|
|
|
|
|
hash_key_name |
290
|
|
|
|
|
|
|
preferences |
291
|
|
|
|
|
|
|
transformations |
292
|
|
|
|
|
|
|
)); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub preference { |
297
|
19
|
|
|
19
|
1
|
11681
|
my ($self, @records) = @_; |
298
|
19
|
|
|
|
|
43
|
my $rules = $self->{preferences}; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# return last record if there are no preferences |
301
|
19
|
50
|
33
|
|
|
102
|
return $records[-1] |
302
|
|
|
|
|
|
|
if !$rules || !@$rules; |
303
|
|
|
|
|
|
|
|
304
|
19
|
|
|
|
|
42
|
foreach my $rule ( @$rules ){ |
305
|
33
|
|
|
|
|
84
|
my $template = "[% IF $rule %]1[% ELSE %]0[% END %]"; |
306
|
|
|
|
|
|
|
# reverse records so that if any are equal the last one in wins |
307
|
33
|
|
|
|
|
54
|
foreach my $record ( reverse @records ){ |
308
|
72
|
|
|
|
|
284
|
my $found = $self->{query}->_process_template(\$template, $record); |
309
|
70
|
100
|
|
|
|
305
|
return $record if $found; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
# last record is DBI compatibile plus it is often the newest record |
313
|
2
|
|
|
|
|
11
|
return $records[-1]; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub query { |
318
|
0
|
|
|
0
|
1
|
0
|
return $_[0]->{query}; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub row_count { |
323
|
3
|
|
|
3
|
1
|
1807
|
return $_[0]->{row_count}; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub times { |
328
|
3
|
|
|
3
|
1
|
1678
|
my ($self) = @_; |
329
|
3
|
|
|
|
|
7
|
my %times = %{ $self->{times} }; |
|
3
|
|
|
|
|
19
|
|
330
|
3
|
|
|
|
|
12
|
$times{total} = $times{prepare} + $times{execute} + $times{fetch}; |
331
|
3
|
|
|
|
|
11
|
return \%times; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# The DBI objects clean up after themselves, so DESTROY not currently warranted |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
1; |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
__END__ |