| Filename | /2home/ss5/perl5/perlbrew/perls/perl-5.12.3/lib/site_perl/5.12.3/DBIx/Class/Storage/DBIHacks.pm |
| Statements | Executed 949077 statements in 2.25s |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 7779 | 3 | 2 | 1.50s | 2.20s | DBIx::Class::Storage::DBIHacks::_resolve_column_info |
| 111955 | 5 | 1 | 269ms | 269ms | DBIx::Class::Storage::DBIHacks::CORE:match (opcode) |
| 14485 | 2 | 2 | 222ms | 240ms | DBIx::Class::Storage::DBIHacks::_resolve_ident_sources |
| 6706 | 1 | 1 | 90.0ms | 571ms | DBIx::Class::Storage::DBIHacks::_prune_unused_joins |
| 2102 | 3 | 2 | 58.5ms | 284ms | DBIx::Class::Storage::DBIHacks::_extract_order_criteria |
| 2102 | 2 | 1 | 46.6ms | 188ms | DBIx::Class::Storage::DBIHacks::__ANON__[:667] |
| 147 | 1 | 1 | 42.9ms | 157ms | DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args |
| 6615 | 4 | 1 | 10.8ms | 10.8ms | DBIx::Class::Storage::DBIHacks::CORE:regcomp (opcode) |
| 147 | 1 | 1 | 4.91ms | 4.91ms | DBIx::Class::Storage::DBIHacks::_inner_join_to_node |
| 1323 | 2 | 1 | 1.52ms | 1.52ms | DBIx::Class::Storage::DBIHacks::CORE:qr (opcode) |
| 152 | 1 | 1 | 400µs | 400µs | DBIx::Class::Storage::DBIHacks::CORE:subst (opcode) |
| 1 | 1 | 1 | 29µs | 353µs | DBIx::Class::Storage::DBIHacks::_group_over_selection |
| 1 | 1 | 1 | 13µs | 33µs | DBIx::Class::Storage::DBIHacks::BEGIN@609 |
| 1 | 1 | 1 | 13µs | 15µs | DBIx::Class::Storage::DBIHacks::BEGIN@10 |
| 1 | 1 | 1 | 12µs | 49µs | DBIx::Class::Storage::DBIHacks::BEGIN@16 |
| 1 | 1 | 1 | 11µs | 28µs | DBIx::Class::Storage::DBIHacks::BEGIN@18 |
| 1 | 1 | 1 | 11µs | 20µs | DBIx::Class::Storage::DBIHacks::BEGIN@14 |
| 1 | 1 | 1 | 9µs | 192µs | DBIx::Class::Storage::DBIHacks::BEGIN@19 |
| 1 | 1 | 1 | 9µs | 5.52ms | DBIx::Class::Storage::DBIHacks::BEGIN@13 |
| 1 | 1 | 1 | 8µs | 30µs | DBIx::Class::Storage::DBIHacks::BEGIN@17 |
| 1 | 1 | 1 | 7µs | 15µs | DBIx::Class::Storage::DBIHacks::BEGIN@11 |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBIHacks::__ANON__[:139] |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBIHacks::__ANON__[:632] |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBIHacks::__ANON__[:636] |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBIHacks::_adjust_select_args_for_complex_prefetch |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBIHacks::_extract_condition_columns |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package #hide from PAUSE | ||||
| 2 | DBIx::Class::Storage::DBIHacks; | ||||
| 3 | |||||
| 4 | # | ||||
| 5 | # This module contains code that should never have seen the light of day, | ||||
| 6 | # does not belong in the Storage, or is otherwise unfit for public | ||||
| 7 | # display. The arrival of SQLA2 should immediately obsolete 90% of this | ||||
| 8 | # | ||||
| 9 | |||||
| 10 | 3 | 18µs | 2 | 17µs | # spent 15µs (13+2) within DBIx::Class::Storage::DBIHacks::BEGIN@10 which was called:
# once (13µs+2µs) by base::import at line 10 # spent 15µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@10
# spent 2µs making 1 call to strict::import |
| 11 | 3 | 20µs | 2 | 22µs | # spent 15µs (7+8) within DBIx::Class::Storage::DBIHacks::BEGIN@11 which was called:
# once (7µs+8µs) by base::import at line 11 # spent 15µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@11
# spent 8µs making 1 call to warnings::import |
| 12 | |||||
| 13 | 3 | 34µs | 2 | 5.52ms | # spent 5.52ms (9µs+5.51) within DBIx::Class::Storage::DBIHacks::BEGIN@13 which was called:
# once (9µs+5.51ms) by base::import at line 13 # spent 5.52ms making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@13
# spent 5.51ms making 1 call to base::import, recursion: max depth 1, sum of overlapping time 5.51ms |
| 14 | 3 | 23µs | 2 | 30µs | # spent 20µs (11+10) within DBIx::Class::Storage::DBIHacks::BEGIN@14 which was called:
# once (11µs+10µs) by base::import at line 14 # spent 20µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@14
# spent 10µs making 1 call to mro::import |
| 15 | |||||
| 16 | 3 | 25µs | 2 | 86µs | # spent 49µs (12+37) within DBIx::Class::Storage::DBIHacks::BEGIN@16 which was called:
# once (12µs+37µs) by base::import at line 16 # spent 49µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@16
# spent 37µs making 1 call to Exporter::import |
| 17 | 3 | 19µs | 2 | 52µs | # spent 30µs (8+22) within DBIx::Class::Storage::DBIHacks::BEGIN@17 which was called:
# once (8µs+22µs) by base::import at line 17 # spent 30µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@17
# spent 22µs making 1 call to Exporter::import |
| 18 | 3 | 21µs | 2 | 46µs | # spent 28µs (11+18) within DBIx::Class::Storage::DBIHacks::BEGIN@18 which was called:
# once (11µs+18µs) by base::import at line 18 # spent 28µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@18
# spent 18µs making 1 call to Exporter::import |
| 19 | 3 | 1.97ms | 2 | 375µs | # spent 192µs (9+183) within DBIx::Class::Storage::DBIHacks::BEGIN@19 which was called:
# once (9µs+183µs) by base::import at line 19 # spent 192µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@19
# spent 183µs making 1 call to namespace::clean::import |
| 20 | |||||
| 21 | # | ||||
| 22 | # This code will remove non-selecting/non-restricting joins from | ||||
| 23 | # {from} specs, aiding the RDBMS query optimizer | ||||
| 24 | # | ||||
| 25 | # spent 571ms (90.0+481) within DBIx::Class::Storage::DBIHacks::_prune_unused_joins which was called 6706 times, avg 85µs/call:
# 6706 times (90.0ms+481ms) by DBIx::Class::Storage::DBI::_select_args at line 2165 of DBIx/Class/Storage/DBI.pm, avg 85µs/call | ||||
| 26 | 29764 | 87.6ms | my $self = shift; | ||
| 27 | my ($from, $select, $where, $attrs) = @_; | ||||
| 28 | |||||
| 29 | 6706 | 324ms | return $from unless $self->_use_join_optimizer; # spent 324ms making 6706 calls to DBIx::Class::Storage::DBI::_use_join_optimizer, avg 48µs/call | ||
| 30 | |||||
| 31 | if (ref $from ne 'ARRAY' || ref $from->[0] ne 'HASH' || ref $from->[1] ne 'ARRAY') { | ||||
| 32 | return $from; # only standard {from} specs are supported | ||||
| 33 | } | ||||
| 34 | |||||
| 35 | 147 | 157ms | my $aliastypes = $self->_resolve_aliastypes_from_select_args(@_); # spent 157ms making 147 calls to DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args, avg 1.07ms/call | ||
| 36 | |||||
| 37 | # a grouped set will not be affected by amount of rows. Thus any | ||||
| 38 | # {multiplying} joins can go | ||||
| 39 | delete $aliastypes->{multiplying} if $attrs->{group_by}; | ||||
| 40 | |||||
| 41 | my @newfrom = $from->[0]; # FROM head is always present | ||||
| 42 | |||||
| 43 | my %need_joins; | ||||
| 44 | for (values %$aliastypes) { | ||||
| 45 | # add all requested aliases | ||||
| 46 | $need_joins{$_} = 1 for keys %$_; | ||||
| 47 | |||||
| 48 | # add all their parents (as per joinpath which is an AoH { table => alias }) | ||||
| 49 | $need_joins{$_} = 1 for map { values %$_ } map { @$_ } values %$_; | ||||
| 50 | } | ||||
| 51 | for my $j (@{$from}[1..$#$from]) { | ||||
| 52 | push @newfrom, $j if ( | ||||
| 53 | (! $j->[0]{-alias}) # legacy crap | ||||
| 54 | || | ||||
| 55 | $need_joins{$j->[0]{-alias}} | ||||
| 56 | ); | ||||
| 57 | } | ||||
| 58 | |||||
| 59 | return \@newfrom; | ||||
| 60 | } | ||||
| 61 | |||||
| 62 | # | ||||
| 63 | # This is the code producing joined subqueries like: | ||||
| 64 | # SELECT me.*, other.* FROM ( SELECT me.* FROM ... ) JOIN other ON ... | ||||
| 65 | # | ||||
| 66 | sub _adjust_select_args_for_complex_prefetch { | ||||
| 67 | my ($self, $from, $select, $where, $attrs) = @_; | ||||
| 68 | |||||
| 69 | $self->throw_exception ('Nothing to prefetch... how did we get here?!') | ||||
| 70 | if not @{$attrs->{_prefetch_selector_range}}; | ||||
| 71 | |||||
| 72 | $self->throw_exception ('Complex prefetches are not supported on resultsets with a custom from attribute') | ||||
| 73 | if (ref $from ne 'ARRAY' || ref $from->[0] ne 'HASH' || ref $from->[1] ne 'ARRAY'); | ||||
| 74 | |||||
| 75 | |||||
| 76 | # generate inner/outer attribute lists, remove stuff that doesn't apply | ||||
| 77 | my $outer_attrs = { %$attrs }; | ||||
| 78 | delete $outer_attrs->{$_} for qw/where bind rows offset group_by having/; | ||||
| 79 | |||||
| 80 | my $inner_attrs = { %$attrs }; | ||||
| 81 | delete $inner_attrs->{$_} for qw/for collapse _prefetch_selector_range _collapse_order_by select as/; | ||||
| 82 | |||||
| 83 | |||||
| 84 | # bring over all non-collapse-induced order_by into the inner query (if any) | ||||
| 85 | # the outer one will have to keep them all | ||||
| 86 | delete $inner_attrs->{order_by}; | ||||
| 87 | if (my $ord_cnt = @{$outer_attrs->{order_by}} - @{$outer_attrs->{_collapse_order_by}} ) { | ||||
| 88 | $inner_attrs->{order_by} = [ | ||||
| 89 | @{$outer_attrs->{order_by}}[ 0 .. $ord_cnt - 1] | ||||
| 90 | ]; | ||||
| 91 | } | ||||
| 92 | |||||
| 93 | # generate the inner/outer select lists | ||||
| 94 | # for inside we consider only stuff *not* brought in by the prefetch | ||||
| 95 | # on the outside we substitute any function for its alias | ||||
| 96 | my $outer_select = [ @$select ]; | ||||
| 97 | my $inner_select = []; | ||||
| 98 | |||||
| 99 | my ($p_start, $p_end) = @{$outer_attrs->{_prefetch_selector_range}}; | ||||
| 100 | for my $i (0 .. $p_start - 1, $p_end + 1 .. $#$outer_select) { | ||||
| 101 | my $sel = $outer_select->[$i]; | ||||
| 102 | |||||
| 103 | if (ref $sel eq 'HASH' ) { | ||||
| 104 | $sel->{-as} ||= $attrs->{as}[$i]; | ||||
| 105 | $outer_select->[$i] = join ('.', $attrs->{alias}, ($sel->{-as} || "inner_column_$i") ); | ||||
| 106 | } | ||||
| 107 | |||||
| 108 | push @$inner_select, $sel; | ||||
| 109 | |||||
| 110 | push @{$inner_attrs->{as}}, $attrs->{as}[$i]; | ||||
| 111 | } | ||||
| 112 | |||||
| 113 | # construct the inner $from and lock it in a subquery | ||||
| 114 | # we need to prune first, because this will determine if we need a group_by below | ||||
| 115 | # the fake group_by is so that the pruner throws away all non-selecting, non-restricting | ||||
| 116 | # multijoins (since we def. do not care about those inside the subquery) | ||||
| 117 | |||||
| 118 | my $inner_subq = do { | ||||
| 119 | |||||
| 120 | # must use it here regardless of user requests | ||||
| 121 | local $self->{_use_join_optimizer} = 1; | ||||
| 122 | |||||
| 123 | my $inner_from = $self->_prune_unused_joins ($from, $inner_select, $where, { | ||||
| 124 | group_by => ['dummy'], %$inner_attrs, | ||||
| 125 | }); | ||||
| 126 | |||||
| 127 | my $inner_aliastypes = | ||||
| 128 | $self->_resolve_aliastypes_from_select_args( $inner_from, $inner_select, $where, $inner_attrs ); | ||||
| 129 | |||||
| 130 | # we need to simulate collapse in the subq if a multiplying join is pulled | ||||
| 131 | # by being a non-selecting restrictor | ||||
| 132 | if ( | ||||
| 133 | ! $inner_attrs->{group_by} | ||||
| 134 | and | ||||
| 135 | first { | ||||
| 136 | $inner_aliastypes->{restricting}{$_} | ||||
| 137 | and | ||||
| 138 | ! $inner_aliastypes->{selecting}{$_} | ||||
| 139 | } ( keys %{$inner_aliastypes->{multiplying}||{}} ) | ||||
| 140 | ) { | ||||
| 141 | my $unprocessed_order_chunks; | ||||
| 142 | ($inner_attrs->{group_by}, $unprocessed_order_chunks) = $self->_group_over_selection ( | ||||
| 143 | $inner_from, $inner_select, $inner_attrs->{order_by} | ||||
| 144 | ); | ||||
| 145 | |||||
| 146 | $self->throw_exception ( | ||||
| 147 | 'A required group_by clause could not be constructed automatically due to a complex ' | ||||
| 148 | . 'order_by criteria. Either order_by columns only (no functions) or construct a suitable ' | ||||
| 149 | . 'group_by by hand' | ||||
| 150 | ) if $unprocessed_order_chunks; | ||||
| 151 | } | ||||
| 152 | |||||
| 153 | # we already optimized $inner_from above | ||||
| 154 | local $self->{_use_join_optimizer} = 0; | ||||
| 155 | |||||
| 156 | # generate the subquery | ||||
| 157 | $self->_select_args_to_query ( | ||||
| 158 | $inner_from, | ||||
| 159 | $inner_select, | ||||
| 160 | $where, | ||||
| 161 | $inner_attrs, | ||||
| 162 | ); | ||||
| 163 | }; | ||||
| 164 | |||||
| 165 | # Generate the outer from - this is relatively easy (really just replace | ||||
| 166 | # the join slot with the subquery), with a major caveat - we can not | ||||
| 167 | # join anything that is non-selecting (not part of the prefetch), but at | ||||
| 168 | # the same time is a multi-type relationship, as it will explode the result. | ||||
| 169 | # | ||||
| 170 | # There are two possibilities here | ||||
| 171 | # - either the join is non-restricting, in which case we simply throw it away | ||||
| 172 | # - it is part of the restrictions, in which case we need to collapse the outer | ||||
| 173 | # result by tackling yet another group_by to the outside of the query | ||||
| 174 | |||||
| 175 | $from = [ @$from ]; | ||||
| 176 | |||||
| 177 | # so first generate the outer_from, up to the substitution point | ||||
| 178 | my @outer_from; | ||||
| 179 | while (my $j = shift @$from) { | ||||
| 180 | $j = [ $j ] unless ref $j eq 'ARRAY'; # promote the head-from to an AoH | ||||
| 181 | |||||
| 182 | if ($j->[0]{-alias} eq $attrs->{alias}) { # time to swap | ||||
| 183 | |||||
| 184 | push @outer_from, [ | ||||
| 185 | { | ||||
| 186 | -alias => $attrs->{alias}, | ||||
| 187 | -rsrc => $j->[0]{-rsrc}, | ||||
| 188 | $attrs->{alias} => $inner_subq, | ||||
| 189 | }, | ||||
| 190 | @{$j}[1 .. $#$j], | ||||
| 191 | ]; | ||||
| 192 | last; # we'll take care of what's left in $from below | ||||
| 193 | } | ||||
| 194 | else { | ||||
| 195 | push @outer_from, $j; | ||||
| 196 | } | ||||
| 197 | } | ||||
| 198 | |||||
| 199 | # scan the *remaining* from spec against different attributes, and see which joins are needed | ||||
| 200 | # in what role | ||||
| 201 | my $outer_aliastypes = | ||||
| 202 | $self->_resolve_aliastypes_from_select_args( $from, $outer_select, $where, $outer_attrs ); | ||||
| 203 | |||||
| 204 | # unroll parents | ||||
| 205 | my ($outer_select_chain, $outer_restrict_chain) = map { +{ | ||||
| 206 | map { $_ => 1 } map { values %$_} map { @$_ } values %{ $outer_aliastypes->{$_} || {} } | ||||
| 207 | } } qw/selecting restricting/; | ||||
| 208 | |||||
| 209 | # see what's left - throw away if not selecting/restricting | ||||
| 210 | # also throw in a group_by if a non-selecting multiplier, | ||||
| 211 | # to guard against cross-join explosions | ||||
| 212 | my $need_outer_group_by; | ||||
| 213 | while (my $j = shift @$from) { | ||||
| 214 | my $alias = $j->[0]{-alias}; | ||||
| 215 | |||||
| 216 | if ( | ||||
| 217 | $outer_select_chain->{$alias} | ||||
| 218 | ) { | ||||
| 219 | push @outer_from, $j | ||||
| 220 | } | ||||
| 221 | elsif ($outer_restrict_chain->{$alias}) { | ||||
| 222 | push @outer_from, $j; | ||||
| 223 | $need_outer_group_by ||= $outer_aliastypes->{multiplying}{$alias} ? 1 : 0; | ||||
| 224 | } | ||||
| 225 | } | ||||
| 226 | |||||
| 227 | # demote the outer_from head | ||||
| 228 | $outer_from[0] = $outer_from[0][0]; | ||||
| 229 | |||||
| 230 | if ($need_outer_group_by and ! $outer_attrs->{group_by}) { | ||||
| 231 | |||||
| 232 | my $unprocessed_order_chunks; | ||||
| 233 | ($outer_attrs->{group_by}, $unprocessed_order_chunks) = $self->_group_over_selection ( | ||||
| 234 | \@outer_from, $outer_select, $outer_attrs->{order_by} | ||||
| 235 | ); | ||||
| 236 | |||||
| 237 | $self->throw_exception ( | ||||
| 238 | 'A required group_by clause could not be constructed automatically due to a complex ' | ||||
| 239 | . 'order_by criteria. Either order_by columns only (no functions) or construct a suitable ' | ||||
| 240 | . 'group_by by hand' | ||||
| 241 | ) if $unprocessed_order_chunks; | ||||
| 242 | |||||
| 243 | } | ||||
| 244 | |||||
| 245 | # This is totally horrific - the $where ends up in both the inner and outer query | ||||
| 246 | # Unfortunately not much can be done until SQLA2 introspection arrives, and even | ||||
| 247 | # then if where conditions apply to the *right* side of the prefetch, you may have | ||||
| 248 | # to both filter the inner select (e.g. to apply a limit) and then have to re-filter | ||||
| 249 | # the outer select to exclude joins you didin't want in the first place | ||||
| 250 | # | ||||
| 251 | # OTOH it can be seen as a plus: <ash> (notes that this query would make a DBA cry ;) | ||||
| 252 | return (\@outer_from, $outer_select, $where, $outer_attrs); | ||||
| 253 | } | ||||
| 254 | |||||
| 255 | # | ||||
| 256 | # I KNOW THIS SUCKS! GET SQLA2 OUT THE DOOR SO THIS CAN DIE! | ||||
| 257 | # | ||||
| 258 | # Due to a lack of SQLA2 we fall back to crude scans of all the | ||||
| 259 | # select/where/order/group attributes, in order to determine what | ||||
| 260 | # aliases are neded to fulfill the query. This information is used | ||||
| 261 | # throughout the code to prune unnecessary JOINs from the queries | ||||
| 262 | # in an attempt to reduce the execution time. | ||||
| 263 | # Although the method is pretty horrific, the worst thing that can | ||||
| 264 | # happen is for it to fail due to some scalar SQL, which in turn will | ||||
| 265 | # result in a vocal exception. | ||||
| 266 | # spent 157ms (42.9+114) within DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args which was called 147 times, avg 1.07ms/call:
# 147 times (42.9ms+114ms) by DBIx::Class::Storage::DBIHacks::_prune_unused_joins at line 35, avg 1.07ms/call | ||||
| 267 | 18522 | 59.9ms | my ( $self, $from, $select, $where, $attrs ) = @_; | ||
| 268 | |||||
| 269 | $self->throw_exception ('Unable to analyze custom {from}') | ||||
| 270 | if ref $from ne 'ARRAY'; | ||||
| 271 | |||||
| 272 | # what we will return | ||||
| 273 | my $aliases_by_type; | ||||
| 274 | |||||
| 275 | # see what aliases are there to work with | ||||
| 276 | my $alias_list; | ||||
| 277 | for (@$from) { | ||||
| 278 | my $j = $_; | ||||
| 279 | $j = $j->[0] if ref $j eq 'ARRAY'; | ||||
| 280 | my $al = $j->{-alias} | ||||
| 281 | or next; | ||||
| 282 | |||||
| 283 | $alias_list->{$al} = $j; | ||||
| 284 | $aliases_by_type->{multiplying}{$al} ||= $j->{-join_path}||[] if ( | ||||
| 285 | # not array == {from} head == can't be multiplying | ||||
| 286 | ( ref($_) eq 'ARRAY' and ! $j->{-is_single} ) | ||||
| 287 | or | ||||
| 288 | # a parent of ours is already a multiplier | ||||
| 289 | ( grep { $aliases_by_type->{multiplying}{$_} } @{ $j->{-join_path}||[] } ) | ||||
| 290 | ); | ||||
| 291 | } | ||||
| 292 | |||||
| 293 | # get a column to source/alias map (including unqualified ones) | ||||
| 294 | 147 | 35.4ms | my $colinfo = $self->_resolve_column_info ($from); # spent 35.4ms making 147 calls to DBIx::Class::Storage::DBIHacks::_resolve_column_info, avg 241µs/call | ||
| 295 | |||||
| 296 | # set up a botched SQLA | ||||
| 297 | 147 | 305µs | my $sql_maker = $self->sql_maker; # spent 305µs making 147 calls to DBIx::Class::Storage::DBI::sql_maker, avg 2µs/call | ||
| 298 | |||||
| 299 | # these are throw away results, do not pollute the bind stack | ||||
| 300 | local $sql_maker->{select_bind}; | ||||
| 301 | local $sql_maker->{where_bind}; | ||||
| 302 | local $sql_maker->{group_bind}; | ||||
| 303 | local $sql_maker->{having_bind}; | ||||
| 304 | |||||
| 305 | # we can't scan properly without any quoting (\b doesn't cut it | ||||
| 306 | # everywhere), so unless there is proper quoting set - use our | ||||
| 307 | # own weird impossible character. | ||||
| 308 | # Also in the case of no quoting, we need to explicitly disable | ||||
| 309 | # name_sep, otherwise sorry nasty legacy syntax like | ||||
| 310 | # { 'count(foo.id)' => { '>' => 3 } } will stop working >:( | ||||
| 311 | local $sql_maker->{quote_char} = $sql_maker->{quote_char}; | ||||
| 312 | local $sql_maker->{name_sep} = $sql_maker->{name_sep}; | ||||
| 313 | |||||
| 314 | unless (defined $sql_maker->{quote_char} and length $sql_maker->{quote_char}) { | ||||
| 315 | $sql_maker->{quote_char} = ["\x00", "\xFF"]; | ||||
| 316 | # if we don't unset it we screw up retarded but unfortunately working | ||||
| 317 | # 'MAX(foo.bar)' => { '>', 3 } | ||||
| 318 | $sql_maker->{name_sep} = ''; | ||||
| 319 | } | ||||
| 320 | |||||
| 321 | 149 | 1.28ms | my ($lquote, $rquote, $sep) = map { quotemeta $_ } ($sql_maker->_quote_chars, $sql_maker->name_sep); # spent 1.13ms making 147 calls to DBIx::Class::SQLMaker::_quote_chars, avg 8µs/call
# spent 153µs making 1 call to DBIx::Class::SQLMaker::name_sep
# spent 2µs making 1 call to DBIx::Class::SQLMaker::SQLite::name_sep | ||
| 322 | |||||
| 323 | # generate sql chunks | ||||
| 324 | my $to_scan = { | ||||
| 325 | restricting => [ | ||||
| 326 | $sql_maker->_recurse_where ($where), | ||||
| 327 | $sql_maker->_parse_rs_attrs ({ | ||||
| 328 | map { $_ => $attrs->{$_} } (qw/group_by having/) | ||||
| 329 | }), | ||||
| 330 | ], | ||||
| 331 | selecting => [ | ||||
| 332 | $sql_maker->_recurse_fields ($select), | ||||
| 333 | 588 | 59.0ms | ( map { $_->[0] } $self->_extract_order_criteria ($attrs->{order_by}, $sql_maker) ), # spent 24.7ms making 147 calls to SQL::Abstract::_recurse_where, avg 168µs/call
# spent 21.6ms making 147 calls to DBIx::Class::Storage::DBIHacks::_extract_order_criteria, avg 147µs/call
# spent 12.0ms making 147 calls to DBIx::Class::SQLMaker::_recurse_fields, avg 81µs/call
# spent 814µs making 147 calls to DBIx::Class::SQLMaker::_parse_rs_attrs, avg 6µs/call | ||
| 334 | ], | ||||
| 335 | }; | ||||
| 336 | |||||
| 337 | # throw away empty chunks | ||||
| 338 | $_ = [ map { $_ || () } @$_ ] for values %$to_scan; | ||||
| 339 | |||||
| 340 | # first loop through all fully qualified columns and get the corresponding | ||||
| 341 | # alias (should work even if they are in scalarrefs) | ||||
| 342 | for my $alias (keys %$alias_list) { | ||||
| 343 | 588 | 3.75ms | my $al_re = qr/ # spent 3.08ms making 294 calls to DBIx::Class::Storage::DBIHacks::CORE:regcomp, avg 10µs/call
# spent 668µs making 294 calls to DBIx::Class::Storage::DBIHacks::CORE:qr, avg 2µs/call | ||
| 344 | $lquote $alias $rquote $sep | ||||
| 345 | | | ||||
| 346 | \b $alias \. | ||||
| 347 | /x; | ||||
| 348 | |||||
| 349 | for my $type (keys %$to_scan) { | ||||
| 350 | for my $piece (@{$to_scan->{$type}}) { | ||||
| 351 | 2352 | 4.31ms | $aliases_by_type->{$type}{$alias} ||= $alias_list->{$alias}{-join_path}||[] # spent 3.36ms making 1176 calls to DBIx::Class::Storage::DBIHacks::CORE:match, avg 3µs/call
# spent 954µs making 1176 calls to DBIx::Class::Storage::DBIHacks::CORE:regcomp, avg 811ns/call | ||
| 352 | if ($piece =~ $al_re); | ||||
| 353 | } | ||||
| 354 | } | ||||
| 355 | } | ||||
| 356 | |||||
| 357 | # now loop through unqualified column names, and try to locate them within | ||||
| 358 | # the chunks | ||||
| 359 | for my $col (keys %$colinfo) { | ||||
| 360 | 2058 | 703µs | next if $col =~ / \. /x; # if column is qualified it was caught by the above # spent 703µs making 2058 calls to DBIx::Class::Storage::DBIHacks::CORE:match, avg 342ns/call | ||
| 361 | |||||
| 362 | 2058 | 4.86ms | my $col_re = qr/ $lquote $col $rquote /x; # spent 4.01ms making 1029 calls to DBIx::Class::Storage::DBIHacks::CORE:regcomp, avg 4µs/call
# spent 853µs making 1029 calls to DBIx::Class::Storage::DBIHacks::CORE:qr, avg 829ns/call | ||
| 363 | |||||
| 364 | for my $type (keys %$to_scan) { | ||||
| 365 | for my $piece (@{$to_scan->{$type}}) { | ||||
| 366 | 8232 | 4.31ms | if ($piece =~ $col_re) { # spent 2.73ms making 4116 calls to DBIx::Class::Storage::DBIHacks::CORE:regcomp, avg 664ns/call
# spent 1.57ms making 4116 calls to DBIx::Class::Storage::DBIHacks::CORE:match, avg 382ns/call | ||
| 367 | my $alias = $colinfo->{$col}{-source_alias}; | ||||
| 368 | $aliases_by_type->{$type}{$alias} ||= $alias_list->{$alias}{-join_path}||[]; | ||||
| 369 | } | ||||
| 370 | } | ||||
| 371 | } | ||||
| 372 | } | ||||
| 373 | |||||
| 374 | # Add any non-left joins to the restriction list (such joins are indeed restrictions) | ||||
| 375 | for my $j (values %$alias_list) { | ||||
| 376 | my $alias = $j->{-alias} or next; | ||||
| 377 | $aliases_by_type->{restricting}{$alias} ||= $j->{-join_path}||[] if ( | ||||
| 378 | (not $j->{-join_type}) | ||||
| 379 | or | ||||
| 380 | ($j->{-join_type} !~ /^left (?: \s+ outer)? $/xi) | ||||
| 381 | ); | ||||
| 382 | } | ||||
| 383 | |||||
| 384 | return $aliases_by_type; | ||||
| 385 | } | ||||
| 386 | |||||
| 387 | # This is the engine behind { distinct => 1 } | ||||
| 388 | # spent 353µs (29+324) within DBIx::Class::Storage::DBIHacks::_group_over_selection which was called:
# once (29µs+324µs) by DBIx::Class::ResultSet::_resolved_attrs at line 3280 of DBIx/Class/ResultSet.pm | ||||
| 389 | 11 | 28µs | my ($self, $from, $select, $order_by) = @_; | ||
| 390 | |||||
| 391 | 1 | 208µs | my $rs_column_list = $self->_resolve_column_info ($from); # spent 208µs making 1 call to DBIx::Class::Storage::DBIHacks::_resolve_column_info | ||
| 392 | |||||
| 393 | my (@group_by, %group_index); | ||||
| 394 | |||||
| 395 | # the logic is: if it is a { func => val } we assume an aggregate, | ||||
| 396 | # otherwise if \'...' or \[...] we assume the user knows what is | ||||
| 397 | # going on thus group over it | ||||
| 398 | for (@$select) { | ||||
| 399 | if (! ref($_) or ref ($_) ne 'HASH' ) { | ||||
| 400 | push @group_by, $_; | ||||
| 401 | $group_index{$_}++; | ||||
| 402 | 1 | 1µs | if ($rs_column_list->{$_} and $_ !~ /\./ ) { # spent 1µs making 1 call to DBIx::Class::Storage::DBIHacks::CORE:match | ||
| 403 | # add a fully qualified version as well | ||||
| 404 | $group_index{"$rs_column_list->{$_}{-source_alias}.$_"}++; | ||||
| 405 | } | ||||
| 406 | } | ||||
| 407 | } | ||||
| 408 | |||||
| 409 | # add any order_by parts that are not already present in the group_by | ||||
| 410 | # we need to be careful not to add any named functions/aggregates | ||||
| 411 | # i.e. order_by => [ ... { count => 'foo' } ... ] | ||||
| 412 | my @leftovers; | ||||
| 413 | 1 | 115µs | for ($self->_extract_order_criteria($order_by)) { # spent 115µs making 1 call to DBIx::Class::Storage::DBIHacks::_extract_order_criteria | ||
| 414 | # only consider real columns (for functions the user got to do an explicit group_by) | ||||
| 415 | if (@$_ != 1) { | ||||
| 416 | push @leftovers, $_; | ||||
| 417 | next; | ||||
| 418 | } | ||||
| 419 | my $chunk = $_->[0]; | ||||
| 420 | my $colinfo = $rs_column_list->{$chunk} or do { | ||||
| 421 | push @leftovers, $_; | ||||
| 422 | next; | ||||
| 423 | }; | ||||
| 424 | |||||
| 425 | $chunk = "$colinfo->{-source_alias}.$chunk" if $chunk !~ /\./; | ||||
| 426 | push @group_by, $chunk unless $group_index{$chunk}++; | ||||
| 427 | } | ||||
| 428 | |||||
| 429 | return wantarray | ||||
| 430 | ? (\@group_by, (@leftovers ? \@leftovers : undef) ) | ||||
| 431 | : \@group_by | ||||
| 432 | ; | ||||
| 433 | } | ||||
| 434 | |||||
| 435 | # spent 240ms (222+17.7) within DBIx::Class::Storage::DBIHacks::_resolve_ident_sources which was called 14485 times, avg 17µs/call:
# 7779 times (105ms+11.4ms) by DBIx::Class::Storage::DBIHacks::_resolve_column_info at line 477, avg 15µs/call
# 6706 times (118ms+6.34ms) by DBIx::Class::Storage::DBI::_select_args at line 2109 of DBIx/Class/Storage/DBI.pm, avg 19µs/call | ||||
| 436 | 169114 | 264ms | my ($self, $ident) = @_; | ||
| 437 | |||||
| 438 | my $alias2source = {}; | ||||
| 439 | my $rs_alias; | ||||
| 440 | |||||
| 441 | # the reason this is so contrived is that $ident may be a {from} | ||||
| 442 | # structure, specifying multiple tables to join | ||||
| 443 | 15779 | 17.7ms | if ( blessed $ident && $ident->isa("DBIx::Class::ResultSource") ) { # spent 14.0ms making 14485 calls to Scalar::Util::blessed, avg 967ns/call
# spent 3.68ms making 1294 calls to UNIVERSAL::isa, avg 3µs/call | ||
| 444 | # this is compat mode for insert/update/delete which do not deal with aliases | ||||
| 445 | $alias2source->{me} = $ident; | ||||
| 446 | $rs_alias = 'me'; | ||||
| 447 | } | ||||
| 448 | elsif (ref $ident eq 'ARRAY') { | ||||
| 449 | |||||
| 450 | for (@$ident) { | ||||
| 451 | my $tabinfo; | ||||
| 452 | if (ref $_ eq 'HASH') { | ||||
| 453 | $tabinfo = $_; | ||||
| 454 | $rs_alias = $tabinfo->{-alias}; | ||||
| 455 | } | ||||
| 456 | if (ref $_ eq 'ARRAY' and ref $_->[0] eq 'HASH') { | ||||
| 457 | $tabinfo = $_->[0]; | ||||
| 458 | } | ||||
| 459 | |||||
| 460 | $alias2source->{$tabinfo->{-alias}} = $tabinfo->{-rsrc} | ||||
| 461 | if ($tabinfo->{-rsrc}); | ||||
| 462 | } | ||||
| 463 | } | ||||
| 464 | |||||
| 465 | return ($alias2source, $rs_alias); | ||||
| 466 | } | ||||
| 467 | |||||
| 468 | # Takes $ident, \@column_names | ||||
| 469 | # | ||||
| 470 | # returns { $column_name => \%column_info, ... } | ||||
| 471 | # also note: this adds -result_source => $rsrc to the column info | ||||
| 472 | # | ||||
| 473 | # If no columns_names are supplied returns info about *all* columns | ||||
| 474 | # for all sources | ||||
| 475 | # spent 2.20s (1.50+701ms) within DBIx::Class::Storage::DBIHacks::_resolve_column_info which was called 7779 times, avg 283µs/call:
# 7631 times (1.48s+689ms) by DBIx::Class::Storage::DBI::__ANON__[/2home/ss5/perl5/perlbrew/perls/perl-5.12.3/lib/site_perl/5.12.3/DBIx/Class/Storage/DBI.pm:1443] at line 1435 of DBIx/Class/Storage/DBI.pm, avg 284µs/call
# 147 times (23.2ms+12.2ms) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 294, avg 241µs/call
# once (134µs+75µs) by DBIx::Class::Storage::DBIHacks::_group_over_selection at line 391 | ||||
| 476 | 706002 | 1.74s | my ($self, $ident, $colnames) = @_; | ||
| 477 | 7779 | 116ms | my ($alias2src, $root_alias) = $self->_resolve_ident_sources($ident); # spent 116ms making 7779 calls to DBIx::Class::Storage::DBIHacks::_resolve_ident_sources, avg 15µs/call | ||
| 478 | |||||
| 479 | my (%seen_cols, @auto_colnames); | ||||
| 480 | |||||
| 481 | # compile a global list of column names, to be able to properly | ||||
| 482 | # disambiguate unqualified column names (if at all possible) | ||||
| 483 | for my $alias (keys %$alias2src) { | ||||
| 484 | my $rsrc = $alias2src->{$alias}; | ||||
| 485 | 8073 | 49.9ms | for my $colname ($rsrc->columns) { # spent 49.9ms making 8073 calls to DBIx::Class::ResultSource::columns, avg 6µs/call | ||
| 486 | push @{$seen_cols{$colname}}, $alias; | ||||
| 487 | push @auto_colnames, "$alias.$colname" unless $colnames; | ||||
| 488 | } | ||||
| 489 | } | ||||
| 490 | |||||
| 491 | $colnames ||= [ | ||||
| 492 | @auto_colnames, | ||||
| 493 | grep { @{$seen_cols{$_}} == 1 } (keys %seen_cols), | ||||
| 494 | ]; | ||||
| 495 | |||||
| 496 | my (%return, $colinfos); | ||||
| 497 | foreach my $col (@$colnames) { | ||||
| 498 | 104604 | 263ms | my ($source_alias, $colname) = $col =~ m/^ (?: ([^\.]+) \. )? (.+) $/x; # spent 263ms making 104604 calls to DBIx::Class::Storage::DBIHacks::CORE:match, avg 3µs/call | ||
| 499 | |||||
| 500 | # if the column was seen exactly once - we know which rsrc it came from | ||||
| 501 | $source_alias ||= $seen_cols{$colname}[0] | ||||
| 502 | if ($seen_cols{$colname} and @{$seen_cols{$colname}} == 1); | ||||
| 503 | |||||
| 504 | next unless $source_alias; | ||||
| 505 | |||||
| 506 | my $rsrc = $alias2src->{$source_alias} | ||||
| 507 | or next; | ||||
| 508 | |||||
| 509 | $return{$col} = { | ||||
| 510 | %{ | ||||
| 511 | 8073 | 272ms | ( $colinfos->{$source_alias} ||= $rsrc->columns_info )->{$colname} # spent 272ms making 8073 calls to DBIx::Class::ResultSource::columns_info, avg 34µs/call | ||
| 512 | || | ||||
| 513 | $self->throw_exception( | ||||
| 514 | "No such column '$colname' on source " . $rsrc->source_name | ||||
| 515 | ); | ||||
| 516 | }, | ||||
| 517 | -result_source => $rsrc, | ||||
| 518 | -source_alias => $source_alias, | ||||
| 519 | }; | ||||
| 520 | } | ||||
| 521 | |||||
| 522 | return \%return; | ||||
| 523 | } | ||||
| 524 | |||||
| 525 | # The DBIC relationship chaining implementation is pretty simple - every | ||||
| 526 | # new related_relationship is pushed onto the {from} stack, and the {select} | ||||
| 527 | # window simply slides further in. This means that when we count somewhere | ||||
| 528 | # in the middle, we got to make sure that everything in the join chain is an | ||||
| 529 | # actual inner join, otherwise the count will come back with unpredictable | ||||
| 530 | # results (a resultset may be generated with _some_ rows regardless of if | ||||
| 531 | # the relation which the $rs currently selects has rows or not). E.g. | ||||
| 532 | # $artist_rs->cds->count - normally generates: | ||||
| 533 | # SELECT COUNT( * ) FROM artist me LEFT JOIN cd cds ON cds.artist = me.artistid | ||||
| 534 | # which actually returns the number of artists * (number of cds || 1) | ||||
| 535 | # | ||||
| 536 | # So what we do here is crawl {from}, determine if the current alias is at | ||||
| 537 | # the top of the stack, and if not - make sure the chain is inner-joined down | ||||
| 538 | # to the root. | ||||
| 539 | # | ||||
| 540 | # spent 4.91ms within DBIx::Class::Storage::DBIHacks::_inner_join_to_node which was called 147 times, avg 33µs/call:
# 147 times (4.91ms+0s) by DBIx::Class::ResultSet::related_resultset at line 2881 of DBIx/Class/ResultSet.pm, avg 33µs/call | ||||
| 541 | 2499 | 4.92ms | my ($self, $from, $alias) = @_; | ||
| 542 | |||||
| 543 | # subqueries and other oddness are naturally not supported | ||||
| 544 | return $from if ( | ||||
| 545 | ref $from ne 'ARRAY' | ||||
| 546 | || | ||||
| 547 | @$from <= 1 | ||||
| 548 | || | ||||
| 549 | ref $from->[0] ne 'HASH' | ||||
| 550 | || | ||||
| 551 | ! $from->[0]{-alias} | ||||
| 552 | || | ||||
| 553 | $from->[0]{-alias} eq $alias # this last bit means $alias is the head of $from - nothing to do | ||||
| 554 | ); | ||||
| 555 | |||||
| 556 | # find the current $alias in the $from structure | ||||
| 557 | my $switch_branch; | ||||
| 558 | JOINSCAN: | ||||
| 559 | for my $j (@{$from}[1 .. $#$from]) { | ||||
| 560 | if ($j->[0]{-alias} eq $alias) { | ||||
| 561 | $switch_branch = $j->[0]{-join_path}; | ||||
| 562 | last JOINSCAN; | ||||
| 563 | } | ||||
| 564 | } | ||||
| 565 | |||||
| 566 | # something else went quite wrong | ||||
| 567 | return $from unless $switch_branch; | ||||
| 568 | |||||
| 569 | # So it looks like we will have to switch some stuff around. | ||||
| 570 | # local() is useless here as we will be leaving the scope | ||||
| 571 | # anyway, and deep cloning is just too fucking expensive | ||||
| 572 | # So replace the first hashref in the node arrayref manually | ||||
| 573 | my @new_from = ($from->[0]); | ||||
| 574 | my $sw_idx = { map { (values %$_), 1 } @$switch_branch }; #there's one k/v per join-path | ||||
| 575 | |||||
| 576 | for my $j (@{$from}[1 .. $#$from]) { | ||||
| 577 | my $jalias = $j->[0]{-alias}; | ||||
| 578 | |||||
| 579 | if ($sw_idx->{$jalias}) { | ||||
| 580 | my %attrs = %{$j->[0]}; | ||||
| 581 | delete $attrs{-join_type}; | ||||
| 582 | push @new_from, [ | ||||
| 583 | \%attrs, | ||||
| 584 | @{$j}[ 1 .. $#$j ], | ||||
| 585 | ]; | ||||
| 586 | } | ||||
| 587 | else { | ||||
| 588 | push @new_from, $j; | ||||
| 589 | } | ||||
| 590 | } | ||||
| 591 | |||||
| 592 | return \@new_from; | ||||
| 593 | } | ||||
| 594 | |||||
| 595 | # yet another atrocity: attempt to extract all columns from a | ||||
| 596 | # where condition by hooking _quote | ||||
| 597 | sub _extract_condition_columns { | ||||
| 598 | my ($self, $cond, $sql_maker) = @_; | ||||
| 599 | |||||
| 600 | return [] unless $cond; | ||||
| 601 | |||||
| 602 | $sql_maker ||= $self->{_sql_ident_capturer} ||= do { | ||||
| 603 | # FIXME - replace with a Moo trait | ||||
| 604 | my $orig_sm_class = ref $self->sql_maker; | ||||
| 605 | my $smic_class = "${orig_sm_class}::_IdentCapture_"; | ||||
| 606 | |||||
| 607 | unless ($smic_class->isa('SQL::Abstract')) { | ||||
| 608 | |||||
| 609 | 3 | 332µs | 2 | 52µs | # spent 33µs (13+20) within DBIx::Class::Storage::DBIHacks::BEGIN@609 which was called:
# once (13µs+20µs) by base::import at line 609 # spent 33µs making 1 call to DBIx::Class::Storage::DBIHacks::BEGIN@609
# spent 20µs making 1 call to strict::unimport |
| 610 | *{"${smic_class}::_quote"} = subname "${smic_class}::_quote" => sub { | ||||
| 611 | my ($self, $ident) = @_; | ||||
| 612 | if (ref $ident eq 'SCALAR') { | ||||
| 613 | $ident = $$ident; | ||||
| 614 | my $storage_quotes = $self->sql_quote_char || '"'; | ||||
| 615 | my ($ql, $qr) = map | ||||
| 616 | { quotemeta $_ } | ||||
| 617 | (ref $storage_quotes eq 'ARRAY' ? @$storage_quotes : ($storage_quotes) x 2 ) | ||||
| 618 | ; | ||||
| 619 | |||||
| 620 | while ($ident =~ / | ||||
| 621 | $ql (\w+) $qr | ||||
| 622 | | | ||||
| 623 | ([\w\.]+) | ||||
| 624 | /xg) { | ||||
| 625 | $self->{_captured_idents}{$1||$2}++; | ||||
| 626 | } | ||||
| 627 | } | ||||
| 628 | else { | ||||
| 629 | $self->{_captured_idents}{$ident}++; | ||||
| 630 | } | ||||
| 631 | return $ident; | ||||
| 632 | }; | ||||
| 633 | |||||
| 634 | *{"${smic_class}::_get_captured_idents"} = subname "${smic_class}::_get_captures" => sub { | ||||
| 635 | (delete shift->{_captured_idents}) || {}; | ||||
| 636 | }; | ||||
| 637 | |||||
| 638 | $self->inject_base ($smic_class, $orig_sm_class); | ||||
| 639 | |||||
| 640 | } | ||||
| 641 | |||||
| 642 | $smic_class->new(); | ||||
| 643 | }; | ||||
| 644 | |||||
| 645 | $sql_maker->_recurse_where($cond); | ||||
| 646 | |||||
| 647 | return [ sort keys %{$sql_maker->_get_captured_idents} ]; | ||||
| 648 | } | ||||
| 649 | |||||
| 650 | # spent 284ms (58.5+225) within DBIx::Class::Storage::DBIHacks::_extract_order_criteria which was called 2102 times, avg 135µs/call:
# 1954 times (56.6ms+206ms) by DBIx::Class::ResultSetColumn::new at line 68 of DBIx/Class/ResultSetColumn.pm, avg 134µs/call
# 147 times (1.84ms+19.7ms) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 333, avg 147µs/call
# once (25µs+90µs) by DBIx::Class::Storage::DBIHacks::_group_over_selection at line 413 | ||||
| 651 | 12171 | 39.9ms | my ($self, $order_by, $sql_maker) = @_; | ||
| 652 | |||||
| 653 | # spent 188ms (46.6+142) within DBIx::Class::Storage::DBIHacks::__ANON__[/2home/ss5/perl5/perlbrew/perls/perl-5.12.3/lib/site_perl/5.12.3/DBIx/Class/Storage/DBIHacks.pm:667] which was called 2102 times, avg 90µs/call:
# 1955 times (42.4ms+126ms) by DBIx::Class::Storage::DBIHacks::_extract_order_criteria at line 675, avg 86µs/call
# 147 times (4.14ms+15.6ms) by DBIx::Class::Storage::DBIHacks::_extract_order_criteria at line 669, avg 134µs/call | ||||
| 654 | 10966 | 52.5ms | my ($sql_maker, $order_by) = @_; | ||
| 655 | |||||
| 656 | return scalar $sql_maker->_order_by_chunks ($order_by) | ||||
| 657 | unless wantarray; | ||||
| 658 | |||||
| 659 | my @chunks; | ||||
| 660 | 2102 | 141ms | for ($sql_maker->_order_by_chunks ($order_by) ) { # spent 141ms making 2102 calls to SQL::Abstract::_order_by_chunks, avg 67µs/call | ||
| 661 | my $chunk = ref $_ ? $_ : [ $_ ]; | ||||
| 662 | 152 | 400µs | $chunk->[0] =~ s/\s+ (?: ASC|DESC ) \s* $//ix; # spent 400µs making 152 calls to DBIx::Class::Storage::DBIHacks::CORE:subst, avg 3µs/call | ||
| 663 | push @chunks, $chunk; | ||||
| 664 | } | ||||
| 665 | |||||
| 666 | return @chunks; | ||||
| 667 | }; | ||||
| 668 | |||||
| 669 | 147 | 19.7ms | if ($sql_maker) { # spent 19.7ms making 147 calls to DBIx::Class::Storage::DBIHacks::__ANON__[DBIx/Class/Storage/DBIHacks.pm:667], avg 134µs/call | ||
| 670 | return $parser->($sql_maker, $order_by); | ||||
| 671 | } | ||||
| 672 | else { | ||||
| 673 | 1955 | 13.3ms | $sql_maker = $self->sql_maker; # spent 13.3ms making 1955 calls to DBIx::Class::Storage::DBI::sql_maker, avg 7µs/call | ||
| 674 | local $sql_maker->{quote_char}; | ||||
| 675 | 1955 | 168ms | return $parser->($sql_maker, $order_by); # spent 168ms making 1955 calls to DBIx::Class::Storage::DBIHacks::__ANON__[DBIx/Class/Storage/DBIHacks.pm:667], avg 86µs/call | ||
| 676 | } | ||||
| 677 | } | ||||
| 678 | |||||
| 679 | 1 | 12µs | 1 | 234µs | 1; # spent 234µs making 1 call to B::Hooks::EndOfScope::__ANON__[B/Hooks/EndOfScope.pm:26] |
# spent 269ms within DBIx::Class::Storage::DBIHacks::CORE:match which was called 111955 times, avg 2µs/call:
# 104604 times (263ms+0s) by DBIx::Class::Storage::DBIHacks::_resolve_column_info at line 498, avg 3µs/call
# 4116 times (1.57ms+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 366, avg 382ns/call
# 2058 times (703µs+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 360, avg 342ns/call
# 1176 times (3.36ms+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 351, avg 3µs/call
# once (1µs+0s) by DBIx::Class::Storage::DBIHacks::_group_over_selection at line 402 | |||||
# spent 1.52ms within DBIx::Class::Storage::DBIHacks::CORE:qr which was called 1323 times, avg 1µs/call:
# 1029 times (853µs+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 362, avg 829ns/call
# 294 times (668µs+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 343, avg 2µs/call | |||||
# spent 10.8ms within DBIx::Class::Storage::DBIHacks::CORE:regcomp which was called 6615 times, avg 2µs/call:
# 4116 times (2.73ms+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 366, avg 664ns/call
# 1176 times (954µs+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 351, avg 811ns/call
# 1029 times (4.01ms+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 362, avg 4µs/call
# 294 times (3.08ms+0s) by DBIx::Class::Storage::DBIHacks::_resolve_aliastypes_from_select_args at line 343, avg 10µs/call | |||||
# spent 400µs within DBIx::Class::Storage::DBIHacks::CORE:subst which was called 152 times, avg 3µs/call:
# 152 times (400µs+0s) by DBIx::Class::Storage::DBIHacks::__ANON__[/2home/ss5/perl5/perlbrew/perls/perl-5.12.3/lib/site_perl/5.12.3/DBIx/Class/Storage/DBIHacks.pm:667] at line 662, avg 3µs/call |