Filename | /usr/share/perl5/DBIx/Class/SQLMaker.pm |
Statements | Executed 8233 statements in 23.7ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
2598 | 7 | 2 | 35.8ms | 75.5ms | _quote | DBIx::Class::SQLMaker::
1960 | 3 | 1 | 13.0ms | 65.8ms | _recurse_fields (recurses: max depth 2, inclusive time 58.4ms) | DBIx::Class::SQLMaker::
317 | 2 | 2 | 11.5ms | 13.6ms | _order_by | DBIx::Class::SQLMaker::
317 | 1 | 1 | 11.4ms | 283ms | select | DBIx::Class::SQLMaker::
634 | 2 | 1 | 9.10ms | 20.4ms | _from_chunk_to_sql (recurses: max depth 1, inclusive time 8.27ms) | DBIx::Class::SQLMaker::
317 | 1 | 1 | 2.47ms | 2.47ms | _assemble_binds | DBIx::Class::SQLMaker::
317 | 1 | 1 | 2.05ms | 22.4ms | _gen_from_blocks | DBIx::Class::SQLMaker::
317 | 1 | 1 | 1.89ms | 25.9ms | _table | DBIx::Class::SQLMaker::
317 | 1 | 1 | 1.58ms | 24.0ms | _recurse_from | DBIx::Class::SQLMaker::
3796 | 3 | 1 | 1.50ms | 1.50ms | CORE:match (opcode) | DBIx::Class::SQLMaker::
317 | 2 | 2 | 1.37ms | 1.63ms | _parse_rs_attrs | DBIx::Class::SQLMaker::
1 | 1 | 1 | 32µs | 41µs | BEGIN@67 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 14µs | 20µs | BEGIN@3 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 13µs | 30µs | BEGIN@70 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 12µs | 18µs | BEGIN@39 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 11µs | 3.82ms | BEGIN@34 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 11µs | 39µs | BEGIN@41 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 9µs | 50µs | BEGIN@42 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 9µs | 119µs | BEGIN@43 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 8µs | 8µs | _quote_chars | DBIx::Class::SQLMaker::
1 | 1 | 1 | 7µs | 11µs | BEGIN@4 | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | __ANON__[:75] | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | __ANON__[:80] | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | _generate_join_clause | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | _join_condition | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | _lock_select | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | _quoting_enabled | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | _split_order_chunk | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | _where_op_NEST | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | _where_op_multicolumn_in | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | insert | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | throw_exception | DBIx::Class::SQLMaker::
0 | 0 | 0 | 0s | 0s | belch | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | puke | SQL::Abstract::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package DBIx::Class::SQLMaker; | ||||
2 | |||||
3 | 2 | 27µs | # spent 20µs (14+7) within DBIx::Class::SQLMaker::BEGIN@3 which was called:
# once (14µs+7µs) by base::import at line 3 # spent 20µs making 1 call to DBIx::Class::SQLMaker::BEGIN@3
# spent 7µs making 1 call to strict::import | ||
4 | 2 | 14µs | # spent 11µs (7+4) within DBIx::Class::SQLMaker::BEGIN@4 which was called:
# once (7µs+4µs) by base::import at line 4 # spent 11µs making 1 call to DBIx::Class::SQLMaker::BEGIN@4
# spent 4µs making 1 call to warnings::import | ||
5 | |||||
6 | =head1 NAME | ||||
7 | |||||
8 | DBIx::Class::SQLMaker - An SQL::Abstract-based SQL maker class | ||||
9 | |||||
10 | =head1 DESCRIPTION | ||||
11 | |||||
12 | This module is a subclass of L<SQL::Abstract> and includes a number of | ||||
13 | DBIC-specific workarounds, not yet suitable for inclusion into the | ||||
14 | L<SQL::Abstract> core. It also provides all (and more than) the functionality | ||||
15 | of L<SQL::Abstract::Limit>, see L<DBIx::Class::SQLMaker::LimitDialects> for | ||||
16 | more info. | ||||
17 | |||||
18 | Currently the enhancements to L<SQL::Abstract> are: | ||||
19 | |||||
20 | =over | ||||
21 | |||||
22 | =item * Support for C<JOIN> statements (via extended C<table/from> support) | ||||
23 | |||||
24 | =item * Support of functions in C<SELECT> lists | ||||
25 | |||||
26 | =item * C<GROUP BY>/C<HAVING> support (via extensions to the order_by parameter) | ||||
27 | |||||
28 | =item * Support of C<...FOR UPDATE> type of select statement modifiers | ||||
29 | |||||
30 | =back | ||||
31 | |||||
32 | =cut | ||||
33 | |||||
34 | 1 | 0s | # spent 3.82ms (11µs+3.81) within DBIx::Class::SQLMaker::BEGIN@34 which was called:
# once (11µs+3.81ms) by base::import at line 38 # spent 3.81ms making 1 call to base::import, recursion: max depth 1, sum of overlapping time 3.81ms | ||
35 | DBIx::Class::SQLMaker::LimitDialects | ||||
36 | SQL::Abstract | ||||
37 | DBIx::Class | ||||
38 | 1 | 3.82ms | /; # spent 3.82ms making 1 call to DBIx::Class::SQLMaker::BEGIN@34 | ||
39 | 2 | 24µs | # spent 18µs (12+6) within DBIx::Class::SQLMaker::BEGIN@39 which was called:
# once (12µs+6µs) by base::import at line 39 # spent 18µs making 1 call to DBIx::Class::SQLMaker::BEGIN@39
# spent 6µs making 1 call to mro::import | ||
40 | |||||
41 | 2 | 66µs | # spent 39µs (11+28) within DBIx::Class::SQLMaker::BEGIN@41 which was called:
# once (11µs+28µs) by base::import at line 41 # spent 39µs making 1 call to DBIx::Class::SQLMaker::BEGIN@41
# spent 28µs making 1 call to Exporter::import | ||
42 | 2 | 91µs | # spent 50µs (9+41) within DBIx::Class::SQLMaker::BEGIN@42 which was called:
# once (9µs+41µs) by base::import at line 42 # spent 50µs making 1 call to DBIx::Class::SQLMaker::BEGIN@42
# spent 41µs making 1 call to DBIx::Class::Carp::import | ||
43 | 2 | 229µs | # spent 119µs (9+110) within DBIx::Class::SQLMaker::BEGIN@43 which was called:
# once (9µs+110µs) by base::import at line 43 # spent 119µs making 1 call to DBIx::Class::SQLMaker::BEGIN@43
# spent 110µs making 1 call to namespace::clean::import | ||
44 | |||||
45 | 1 | 212µs | __PACKAGE__->mk_group_accessors (simple => qw/quote_char name_sep limit_dialect/); # spent 212µs making 1 call to Class::Accessor::Grouped::mk_group_accessors | ||
46 | |||||
47 | sub _quoting_enabled { | ||||
48 | ( defined $_[0]->{quote_char} and length $_[0]->{quote_char} ) ? 1 : 0 | ||||
49 | } | ||||
50 | |||||
51 | # for when I need a normalized l/r pair | ||||
52 | # spent 8µs within DBIx::Class::SQLMaker::_quote_chars which was called:
# once (8µs+0s) by DBIx::Class::Storage::DBIHacks::_extract_order_criteria at line 879 of DBIx/Class/Storage/DBIHacks.pm | ||||
53 | |||||
54 | # in case we are called in the old !!$sm->_quote_chars fashion | ||||
55 | 1 | 500ns | return () if !wantarray and ( ! defined $_[0]->{quote_char} or ! length $_[0]->{quote_char} ); | ||
56 | |||||
57 | map | ||||
58 | { defined $_ ? $_ : '' } | ||||
59 | 1 | 11µs | ( ref $_[0]->{quote_char} ? (@{$_[0]->{quote_char}}) : ( ($_[0]->{quote_char}) x 2 ) ) | ||
60 | ; | ||||
61 | } | ||||
62 | |||||
63 | # FIXME when we bring in the storage weaklink, check its schema | ||||
64 | # weaklink and channel through $schema->throw_exception | ||||
65 | sub throw_exception { DBIx::Class::Exception->throw($_[1]) } | ||||
66 | |||||
67 | # spent 41µs (32+9) within DBIx::Class::SQLMaker::BEGIN@67 which was called:
# once (32µs+9µs) by base::import at line 81 | ||||
68 | # reinstall the belch()/puke() functions of SQL::Abstract with custom versions | ||||
69 | # that use DBIx::Class::Carp/DBIx::Class::Exception instead of plain Carp | ||||
70 | 2 | 48µs | # spent 30µs (13+18) within DBIx::Class::SQLMaker::BEGIN@70 which was called:
# once (13µs+18µs) by base::import at line 70 # spent 30µs making 1 call to DBIx::Class::SQLMaker::BEGIN@70
# spent 18µs making 1 call to warnings::unimport | ||
71 | |||||
72 | *SQL::Abstract::belch = subname 'SQL::Abstract::belch' => sub (@) { | ||||
73 | my($func) = (caller(1))[3]; | ||||
74 | carp "[$func] Warning: ", @_; | ||||
75 | 1 | 6µs | }; # spent 6µs making 1 call to Sub::Name::subname | ||
76 | |||||
77 | *SQL::Abstract::puke = subname 'SQL::Abstract::puke' => sub (@) { | ||||
78 | my($func) = (caller(1))[3]; | ||||
79 | __PACKAGE__->throw_exception("[$func] Fatal: " . join ('', @_)); | ||||
80 | 1 | 4µs | }; # spent 4µs making 1 call to Sub::Name::subname | ||
81 | 1 | 41µs | } # spent 41µs making 1 call to DBIx::Class::SQLMaker::BEGIN@67 | ||
82 | |||||
83 | # the "oh noes offset/top without limit" constant | ||||
84 | # limited to 31 bits for sanity (and consistency, | ||||
85 | # since it may be handed to the like of sprintf %u) | ||||
86 | # | ||||
87 | # Also *some* builds of SQLite fail the test | ||||
88 | # some_column BETWEEN ? AND ?: 1, 4294967295 | ||||
89 | # with the proper integer bind attrs | ||||
90 | # | ||||
91 | # Implemented as a method, since ::Storage::DBI also | ||||
92 | # refers to it (i.e. for the case of software_limit or | ||||
93 | # as the value to abuse with MSSQL ordered subqueries) | ||||
94 | sub __max_int () { 0x7FFFFFFF }; | ||||
95 | |||||
96 | # we ne longer need to check this - DBIC has ways of dealing with it | ||||
97 | # specifically ::Storage::DBI::_resolve_bindattrs() | ||||
98 | sub _assert_bindval_matches_bindtype () { 1 }; | ||||
99 | |||||
100 | # poor man's de-qualifier | ||||
101 | # spent 75.5ms (35.8+39.7) within DBIx::Class::SQLMaker::_quote which was called 2598 times, avg 29µs/call:
# 1642 times (28.7ms+24.1ms) by DBIx::Class::SQLMaker::_recurse_fields at line 238, avg 32µs/call
# 318 times (2.77ms+9.60ms) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:941] at line 937 of SQL/Abstract.pm, avg 39µs/call
# 317 times (2.31ms+3.39ms) by DBIx::Class::SQLMaker::_from_chunk_to_sql at line 423, avg 18µs/call
# 317 times (1.99ms+2.60ms) by DBIx::Class::SQLMaker::_from_chunk_to_sql at line 442, avg 14µs/call
# 2 times (24µs+19µs) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:1312] at line 1312 of SQL/Abstract.pm, avg 22µs/call
# once (8µs+10µs) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:932] at line 931 of SQL/Abstract.pm
# once (7µs+10µs) by SQL::Abstract::_where_hashpair_UNDEF at line 1051 of SQL/Abstract.pm | ||||
102 | 677 | 6.31ms | 2598 | 8.89ms | $_[0]->next::method( ( $_[0]{_dequalify_idents} and ! ref $_[1] ) # spent 8.89ms making 2598 calls to next::method, avg 3µs/call |
103 | ? $_[1] =~ / ([^\.]+) $ /x | ||||
104 | : $_[1] | ||||
105 | ); | ||||
106 | } | ||||
107 | |||||
108 | sub _where_op_NEST { | ||||
109 | carp_unique ("-nest in search conditions is deprecated, you most probably wanted:\n" | ||||
110 | .q|{..., -and => [ \%cond0, \@cond1, \'cond2', \[ 'cond3', [ col => bind ] ], etc. ], ... }| | ||||
111 | ); | ||||
112 | |||||
113 | shift->next::method(@_); | ||||
114 | } | ||||
115 | |||||
116 | # Handle limit-dialect selection | ||||
117 | # spent 283ms (11.4+272) within DBIx::Class::SQLMaker::select which was called 317 times, avg 893µs/call:
# 317 times (11.4ms+272ms) by DBIx::Class::Storage::DBI::_gen_sql_bind at line 1649 of DBIx/Class/Storage/DBI.pm, avg 893µs/call | ||||
118 | 85 | 71µs | my ($self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_; | ||
119 | |||||
120 | |||||
121 | 85 | 353µs | 317 | 65.8ms | ($fields, @{$self->{select_bind}}) = $self->_recurse_fields($fields); # spent 65.8ms making 317 calls to DBIx::Class::SQLMaker::_recurse_fields, avg 208µs/call |
122 | |||||
123 | 85 | 34µs | if (defined $offset) { | ||
124 | $self->throw_exception('A supplied offset must be a non-negative integer') | ||||
125 | if ( $offset =~ /\D/ or $offset < 0 ); | ||||
126 | } | ||||
127 | 85 | 43µs | $offset ||= 0; | ||
128 | |||||
129 | 85 | 70µs | if (defined $limit) { | ||
130 | 1 | 2µs | $self->throw_exception('A supplied limit must be a positive integer') # spent 2µs making 1 call to DBIx::Class::SQLMaker::CORE:match | ||
131 | if ( $limit =~ /\D/ or $limit <= 0 ); | ||||
132 | } | ||||
133 | elsif ($offset) { | ||||
134 | $limit = $self->__max_int; | ||||
135 | } | ||||
136 | |||||
137 | |||||
138 | 85 | 14µs | my ($sql, @bind); | ||
139 | 85 | 56µs | if ($limit) { | ||
140 | # this is legacy code-flow from SQLA::Limit, it is not set in stone | ||||
141 | |||||
142 | 1 | 4µs | ($sql, @bind) = $self->next::method ($table, $fields, $where); # spent 4µs making 1 call to next::method | ||
143 | |||||
144 | my $limiter; | ||||
145 | |||||
146 | 1 | 21µs | if( $limiter = $self->can ('emulate_limit') ) { # spent 21µs making 1 call to UNIVERSAL::can | ||
147 | carp_unique( | ||||
148 | 'Support for the legacy emulate_limit() mechanism inherited from ' | ||||
149 | . 'SQL::Abstract::Limit has been deprecated, and will be removed when ' | ||||
150 | . 'DBIC transitions to Data::Query. If your code uses this type of ' | ||||
151 | . 'limit specification please file an RT and provide the source of ' | ||||
152 | . 'your emulate_limit() implementation, so an acceptable upgrade-path ' | ||||
153 | . 'can be devised' | ||||
154 | ); | ||||
155 | } | ||||
156 | else { | ||||
157 | 1 | 173µs | my $dialect = $self->limit_dialect # spent 173µs making 1 call to DBIx::Class::SQLMaker::limit_dialect | ||
158 | or $self->throw_exception( "Unable to generate SQL-limit - no limit dialect specified on $self" ); | ||||
159 | |||||
160 | 1 | 4µs | $limiter = $self->can ("_$dialect") # spent 4µs making 1 call to UNIVERSAL::can | ||
161 | or $self->throw_exception(__PACKAGE__ . " does not implement the requested dialect '$dialect'"); | ||||
162 | } | ||||
163 | |||||
164 | $sql = $self->$limiter ( | ||||
165 | $sql, | ||||
166 | 1 | 304µs | { %{$rs_attrs||{}}, _selector_sql => $fields }, # spent 304µs making 1 call to DBIx::Class::SQLMaker::LimitDialects::_LimitXY | ||
167 | $limit, | ||||
168 | $offset | ||||
169 | ); | ||||
170 | } | ||||
171 | else { | ||||
172 | 85 | 759µs | 316 | 1.20ms | ($sql, @bind) = $self->next::method ($table, $fields, $where, $rs_attrs); # spent 1.20ms making 316 calls to next::method, avg 4µs/call |
173 | } | ||||
174 | |||||
175 | 85 | 126µs | push @{$self->{where_bind}}, @bind; | ||
176 | |||||
177 | # this *must* be called, otherwise extra binds will remain in the sql-maker | ||||
178 | 85 | 200µs | 317 | 2.47ms | my @all_bind = $self->_assemble_binds; # spent 2.47ms making 317 calls to DBIx::Class::SQLMaker::_assemble_binds, avg 8µs/call |
179 | |||||
180 | 85 | 40µs | $sql .= $self->_lock_select ($rs_attrs->{for}) | ||
181 | if $rs_attrs->{for}; | ||||
182 | |||||
183 | 85 | 225µs | return wantarray ? ($sql, @all_bind) : $sql; | ||
184 | } | ||||
185 | |||||
186 | # spent 2.47ms within DBIx::Class::SQLMaker::_assemble_binds which was called 317 times, avg 8µs/call:
# 317 times (2.47ms+0s) by DBIx::Class::SQLMaker::select at line 178, avg 8µs/call | ||||
187 | 85 | 35µs | my $self = shift; | ||
188 | 85 | 666µs | return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/pre_select select from where group having order limit/); | ||
189 | } | ||||
190 | |||||
191 | my $for_syntax = { | ||||
192 | update => 'FOR UPDATE', | ||||
193 | shared => 'FOR SHARE', | ||||
194 | }; | ||||
195 | sub _lock_select { | ||||
196 | my ($self, $type) = @_; | ||||
197 | |||||
198 | my $sql; | ||||
199 | if (ref($type) eq 'SCALAR') { | ||||
200 | $sql = "FOR $$type"; | ||||
201 | } | ||||
202 | else { | ||||
203 | $sql = $for_syntax->{$type} || $self->throw_exception( "Unknown SELECT .. FOR type '$type' requested" ); | ||||
204 | } | ||||
205 | |||||
206 | return " $sql"; | ||||
207 | } | ||||
208 | |||||
209 | # Handle default inserts | ||||
210 | sub insert { | ||||
211 | # optimized due to hotttnesss | ||||
212 | # my ($self, $table, $data, $options) = @_; | ||||
213 | |||||
214 | # SQLA will emit INSERT INTO $table ( ) VALUES ( ) | ||||
215 | # which is sadly understood only by MySQL. Change default behavior here, | ||||
216 | # until SQLA2 comes with proper dialect support | ||||
217 | if (! $_[2] or (ref $_[2] eq 'HASH' and !keys %{$_[2]} ) ) { | ||||
218 | my @bind; | ||||
219 | my $sql = sprintf( | ||||
220 | 'INSERT INTO %s DEFAULT VALUES', $_[0]->_quote($_[1]) | ||||
221 | ); | ||||
222 | |||||
223 | if ( ($_[3]||{})->{returning} ) { | ||||
224 | my $s; | ||||
225 | ($s, @bind) = $_[0]->_insert_returning ($_[3]); | ||||
226 | $sql .= $s; | ||||
227 | } | ||||
228 | |||||
229 | return ($sql, @bind); | ||||
230 | } | ||||
231 | |||||
232 | next::method(@_); | ||||
233 | } | ||||
234 | |||||
235 | # spent 65.8ms (13.0+52.8) within DBIx::Class::SQLMaker::_recurse_fields which was called 1960 times, avg 34µs/call:
# 1642 times (5.51ms+-5.51ms) by DBIx::Class::SQLMaker::_recurse_fields at line 244, avg 0s/call
# 317 times (7.48ms+58.3ms) by DBIx::Class::SQLMaker::select at line 121, avg 208µs/call
# once (12µs+-12µs) by DBIx::Class::SQLMaker::_recurse_fields at line 270 | ||||
236 | 507 | 211µs | my ($self, $fields) = @_; | ||
237 | 507 | 175µs | my $ref = ref $fields; | ||
238 | 507 | 1.08ms | 1642 | 52.8ms | return $self->_quote($fields) unless $ref; # spent 52.8ms making 1642 calls to DBIx::Class::SQLMaker::_quote, avg 32µs/call |
239 | 86 | 45µs | return $$fields if $ref eq 'SCALAR'; | ||
240 | |||||
241 | 86 | 40µs | if ($ref eq 'ARRAY') { | ||
242 | 85 | 20µs | my (@select, @bind); | ||
243 | 85 | 87µs | for my $field (@$fields) { | ||
244 | 421 | 696µs | 1642 | 0s | my ($select, @new_bind) = $self->_recurse_fields($field); # spent 58.3ms making 1642 calls to DBIx::Class::SQLMaker::_recurse_fields, avg 36µs/call, recursion: max depth 1, sum of overlapping time 58.3ms |
245 | 421 | 153µs | push @select, $select; | ||
246 | 421 | 252µs | push @bind, @new_bind; | ||
247 | } | ||||
248 | 85 | 303µs | return (join(', ', @select), @bind); | ||
249 | } | ||||
250 | elsif ($ref eq 'HASH') { | ||||
251 | 1 | 3µs | my %hash = %$fields; # shallow copy | ||
252 | |||||
253 | 1 | 1µs | my $as = delete $hash{-as}; # if supplied | ||
254 | |||||
255 | 1 | 1µs | my ($func, $rhs, @toomany) = %hash; | ||
256 | |||||
257 | # there should be only one pair | ||||
258 | 1 | 400ns | if (@toomany) { | ||
259 | $self->throw_exception( "Malformed select argument - too many keys in hash: " . join (',', keys %$fields ) ); | ||||
260 | } | ||||
261 | |||||
262 | 1 | 1µs | if (lc ($func) eq 'distinct' && ref $rhs eq 'ARRAY' && @$rhs > 1) { | ||
263 | $self->throw_exception ( | ||||
264 | 'The select => { distinct => ... } syntax is not supported for multiple columns.' | ||||
265 | .' Instead please use { group_by => [ qw/' . (join ' ', @$rhs) . '/ ] }' | ||||
266 | .' or { select => [ qw/' . (join ' ', @$rhs) . '/ ], distinct => 1 }' | ||||
267 | ); | ||||
268 | } | ||||
269 | |||||
270 | 1 | 9µs | 1 | 0s | my ($rhs_sql, @rhs_bind) = $self->_recurse_fields($rhs); # spent 44µs making 1 call to DBIx::Class::SQLMaker::_recurse_fields, recursion: max depth 2, sum of overlapping time 44µs |
271 | 1 | 6µs | 1 | 3µs | my $select = sprintf ('%s( %s )%s', # spent 3µs making 1 call to SQL::Abstract::_sqlcase |
272 | $self->_sqlcase($func), | ||||
273 | $rhs_sql, | ||||
274 | $as | ||||
275 | ? sprintf (' %s %s', $self->_sqlcase('as'), $self->_quote ($as) ) | ||||
276 | : '' | ||||
277 | ); | ||||
278 | |||||
279 | 1 | 3µs | return ($select, @rhs_bind); | ||
280 | } | ||||
281 | elsif ( $ref eq 'REF' and ref($$fields) eq 'ARRAY' ) { | ||||
282 | return @{$$fields}; | ||||
283 | } | ||||
284 | else { | ||||
285 | $self->throw_exception( $ref . qq{ unexpected in _recurse_fields()} ); | ||||
286 | } | ||||
287 | } | ||||
288 | |||||
289 | |||||
290 | # this used to be a part of _order_by but is broken out for clarity. | ||||
291 | # What we have been doing forever is hijacking the $order arg of | ||||
292 | # SQLA::select to pass in arbitrary pieces of data (first the group_by, | ||||
293 | # then pretty much the entire resultset attr-hash, as more and more | ||||
294 | # things in the SQLA space need to have more info about the $rs they | ||||
295 | # create SQL for. The alternative would be to keep expanding the | ||||
296 | # signature of _select with more and more positional parameters, which | ||||
297 | # is just gross. All hail SQLA2! | ||||
298 | # spent 1.63ms (1.37+266µs) within DBIx::Class::SQLMaker::_parse_rs_attrs which was called 317 times, avg 5µs/call:
# 316 times (1.36ms+0s) by DBIx::Class::SQLMaker::_order_by at line 327, avg 4µs/call
# once (8µs+266µs) by DBIx::Class::SQLMaker::LimitDialects::_LimitXY at line 71 of DBIx/Class/SQLMaker/LimitDialects.pm | ||||
299 | 85 | 34µs | my ($self, $arg) = @_; | ||
300 | |||||
301 | 85 | 42µs | my $sql = ''; | ||
302 | |||||
303 | 85 | 51µs | if ($arg->{group_by}) { | ||
304 | if ( my ($group_sql, @group_bind) = $self->_recurse_fields($arg->{group_by}) ) { | ||||
305 | $sql .= $self->_sqlcase(' group by ') . $group_sql; | ||||
306 | push @{$self->{group_bind}}, @group_bind; | ||||
307 | } | ||||
308 | } | ||||
309 | |||||
310 | 85 | 50µs | if (defined $arg->{having}) { | ||
311 | my ($frag, @bind) = $self->_recurse_where($arg->{having}); | ||||
312 | push(@{$self->{having_bind}}, @bind); | ||||
313 | $sql .= $self->_sqlcase(' having ') . $frag; | ||||
314 | } | ||||
315 | |||||
316 | 85 | 37µs | 1 | 266µs | if (defined $arg->{order_by}) { # spent 266µs making 1 call to DBIx::Class::SQLMaker::_order_by |
317 | $sql .= $self->_order_by ($arg->{order_by}); | ||||
318 | } | ||||
319 | |||||
320 | 85 | 194µs | return $sql; | ||
321 | } | ||||
322 | |||||
323 | # spent 13.6ms (11.5+2.11) within DBIx::Class::SQLMaker::_order_by which was called 317 times, avg 43µs/call:
# 316 times (11.4ms+1.89ms) by SQL::Abstract::where at line 479 of SQL/Abstract.pm, avg 42µs/call
# once (46µs+220µs) by DBIx::Class::SQLMaker::_parse_rs_attrs at line 316 | ||||
324 | 85 | 44µs | my ($self, $arg) = @_; | ||
325 | |||||
326 | # check that we are not called in legacy mode (order_by as 4th argument) | ||||
327 | 850 | 6.44ms | 3160 | 1.89ms | if (ref $arg eq 'HASH' and not grep { $_ =~ /^-(?:desc|asc)/i } keys %$arg ) { # spent 1.36ms making 316 calls to DBIx::Class::SQLMaker::_parse_rs_attrs, avg 4µs/call
# spent 531µs making 2844 calls to DBIx::Class::SQLMaker::CORE:match, avg 187ns/call |
328 | return $self->_parse_rs_attrs ($arg); | ||||
329 | } | ||||
330 | else { | ||||
331 | 1 | 11µs | my ($sql, @bind) = $self->next::method($arg); # spent 11µs making 1 call to next::method | ||
332 | push @{$self->{order_bind}}, @bind; | ||||
333 | return $sql; | ||||
334 | } | ||||
335 | } | ||||
336 | |||||
337 | sub _split_order_chunk { | ||||
338 | my ($self, $chunk) = @_; | ||||
339 | |||||
340 | # strip off sort modifiers, but always succeed, so $1 gets reset | ||||
341 | $chunk =~ s/ (?: \s+ (ASC|DESC) )? \s* $//ix; | ||||
342 | |||||
343 | return ( | ||||
344 | $chunk, | ||||
345 | ( $1 and uc($1) eq 'DESC' ) ? 1 : 0, | ||||
346 | ); | ||||
347 | } | ||||
348 | |||||
349 | # spent 25.9ms (1.89+24.0) within DBIx::Class::SQLMaker::_table which was called 317 times, avg 82µs/call:
# 317 times (1.89ms+24.0ms) by DBIx::Class::SQLMaker::select at line 430 of SQL/Abstract.pm, avg 82µs/call | ||||
350 | # optimized due to hotttnesss | ||||
351 | # my ($self, $from) = @_; | ||||
352 | 85 | 86µs | if (my $ref = ref $_[1] ) { | ||
353 | 85 | 385µs | 317 | 24.0ms | if ($ref eq 'ARRAY') { # spent 24.0ms making 317 calls to DBIx::Class::SQLMaker::_recurse_from, avg 76µs/call |
354 | return $_[0]->_recurse_from(@{$_[1]}); | ||||
355 | } | ||||
356 | elsif ($ref eq 'HASH') { | ||||
357 | return $_[0]->_recurse_from($_[1]); | ||||
358 | } | ||||
359 | elsif ($ref eq 'REF' && ref ${$_[1]} eq 'ARRAY') { | ||||
360 | my ($sql, @bind) = @{ ${$_[1]} }; | ||||
361 | push @{$_[0]->{from_bind}}, @bind; | ||||
362 | return $sql | ||||
363 | } | ||||
364 | } | ||||
365 | return $_[0]->next::method ($_[1]); | ||||
366 | } | ||||
367 | |||||
368 | sub _generate_join_clause { | ||||
369 | my ($self, $join_type) = @_; | ||||
370 | |||||
371 | $join_type = $self->{_default_jointype} | ||||
372 | if ! defined $join_type; | ||||
373 | |||||
374 | return sprintf ('%s JOIN ', | ||||
375 | $join_type ? $self->_sqlcase($join_type) : '' | ||||
376 | ); | ||||
377 | } | ||||
378 | |||||
379 | # spent 24.0ms (1.58+22.4) within DBIx::Class::SQLMaker::_recurse_from which was called 317 times, avg 76µs/call:
# 317 times (1.58ms+22.4ms) by DBIx::Class::SQLMaker::_table at line 353, avg 76µs/call | ||||
380 | 85 | 25µs | my $self = shift; | ||
381 | 85 | 316µs | 317 | 22.4ms | return join (' ', $self->_gen_from_blocks(@_) ); # spent 22.4ms making 317 calls to DBIx::Class::SQLMaker::_gen_from_blocks, avg 71µs/call |
382 | } | ||||
383 | |||||
384 | # spent 22.4ms (2.05+20.4) within DBIx::Class::SQLMaker::_gen_from_blocks which was called 317 times, avg 71µs/call:
# 317 times (2.05ms+20.4ms) by DBIx::Class::SQLMaker::_recurse_from at line 381, avg 71µs/call | ||||
385 | 85 | 50µs | my ($self, $from, @joins) = @_; | ||
386 | |||||
387 | 85 | 219µs | 317 | 20.4ms | my @fchunks = $self->_from_chunk_to_sql($from); # spent 20.4ms making 317 calls to DBIx::Class::SQLMaker::_from_chunk_to_sql, avg 64µs/call |
388 | |||||
389 | 85 | 84µs | for (@joins) { | ||
390 | my ($to, $on) = @$_; | ||||
391 | |||||
392 | # check whether a join type exists | ||||
393 | my $to_jt = ref($to) eq 'ARRAY' ? $to->[0] : $to; | ||||
394 | my $join_type; | ||||
395 | if (ref($to_jt) eq 'HASH' and defined($to_jt->{-join_type})) { | ||||
396 | $join_type = $to_jt->{-join_type}; | ||||
397 | $join_type =~ s/^\s+ | \s+$//xg; | ||||
398 | } | ||||
399 | |||||
400 | my @j = $self->_generate_join_clause( $join_type ); | ||||
401 | |||||
402 | if (ref $to eq 'ARRAY') { | ||||
403 | push(@j, '(', $self->_recurse_from(@$to), ')'); | ||||
404 | } | ||||
405 | else { | ||||
406 | push(@j, $self->_from_chunk_to_sql($to)); | ||||
407 | } | ||||
408 | |||||
409 | my ($sql, @bind) = $self->_join_condition($on); | ||||
410 | push(@j, ' ON ', $sql); | ||||
411 | push @{$self->{from_bind}}, @bind; | ||||
412 | |||||
413 | push @fchunks, join '', @j; | ||||
414 | } | ||||
415 | |||||
416 | 85 | 232µs | return @fchunks; | ||
417 | } | ||||
418 | |||||
419 | # spent 20.4ms (9.10+11.3) within DBIx::Class::SQLMaker::_from_chunk_to_sql which was called 634 times, avg 32µs/call:
# 317 times (6.53ms+13.8ms) by DBIx::Class::SQLMaker::_gen_from_blocks at line 387, avg 64µs/call
# 317 times (2.57ms+-2.57ms) by DBIx::Class::SQLMaker::_from_chunk_to_sql at line 442, avg 0s/call | ||||
420 | 170 | 118µs | my ($self, $fromspec) = @_; | ||
421 | |||||
422 | 170 | 446µs | return join (' ', do { | ||
423 | 170 | 1.34ms | 317 | 5.70ms | if (! ref $fromspec) { # spent 5.70ms making 317 calls to DBIx::Class::SQLMaker::_quote, avg 18µs/call |
424 | $self->_quote($fromspec); | ||||
425 | } | ||||
426 | elsif (ref $fromspec eq 'SCALAR') { | ||||
427 | $$fromspec; | ||||
428 | } | ||||
429 | elsif (ref $fromspec eq 'REF' and ref $$fromspec eq 'ARRAY') { | ||||
430 | push @{$self->{from_bind}}, @{$$fromspec}[1..$#$$fromspec]; | ||||
431 | $$fromspec->[0]; | ||||
432 | } | ||||
433 | elsif (ref $fromspec eq 'HASH') { | ||||
434 | my ($as, $table, $toomuch) = ( map | ||||
435 | 255 | 653µs | 951 | 970µs | { $_ => $fromspec->{$_} } # spent 970µs making 951 calls to DBIx::Class::SQLMaker::CORE:match, avg 1µs/call |
436 | 85 | 421µs | ( grep { $_ !~ /^\-/ } keys %$fromspec ) | ||
437 | ); | ||||
438 | |||||
439 | 85 | 28µs | $self->throw_exception( "Only one table/as pair expected in from-spec but an exra '$toomuch' key present" ) | ||
440 | if defined $toomuch; | ||||
441 | |||||
442 | 85 | 257µs | 634 | 4.59ms | ($self->_from_chunk_to_sql($table), $self->_quote($as) ); # spent 4.59ms making 317 calls to DBIx::Class::SQLMaker::_quote, avg 14µs/call
# spent 8.27ms making 317 calls to DBIx::Class::SQLMaker::_from_chunk_to_sql, avg 26µs/call, recursion: max depth 1, sum of overlapping time 8.27ms |
443 | } | ||||
444 | else { | ||||
445 | $self->throw_exception('Unsupported from refkind: ' . ref $fromspec ); | ||||
446 | } | ||||
447 | }); | ||||
448 | } | ||||
449 | |||||
450 | sub _join_condition { | ||||
451 | my ($self, $cond) = @_; | ||||
452 | |||||
453 | # Backcompat for the old days when a plain hashref | ||||
454 | # { 't1.col1' => 't2.col2' } meant ON t1.col1 = t2.col2 | ||||
455 | if ( | ||||
456 | ref $cond eq 'HASH' | ||||
457 | and | ||||
458 | keys %$cond == 1 | ||||
459 | and | ||||
460 | (keys %$cond)[0] =~ /\./ | ||||
461 | and | ||||
462 | ! ref ( (values %$cond)[0] ) | ||||
463 | ) { | ||||
464 | carp_unique( | ||||
465 | "ResultSet {from} structures with conditions not conforming to the " | ||||
466 | . "SQL::Abstract syntax are deprecated: you either need to stop abusing " | ||||
467 | . "{from} altogether, or express the condition properly using the " | ||||
468 | . "{ -ident => ... } operator" | ||||
469 | ); | ||||
470 | $cond = { keys %$cond => { -ident => values %$cond } } | ||||
471 | } | ||||
472 | elsif ( ref $cond eq 'ARRAY' ) { | ||||
473 | # do our own ORing so that the hashref-shim above is invoked | ||||
474 | my @parts; | ||||
475 | my @binds; | ||||
476 | foreach my $c (@$cond) { | ||||
477 | my ($sql, @bind) = $self->_join_condition($c); | ||||
478 | push @binds, @bind; | ||||
479 | push @parts, $sql; | ||||
480 | } | ||||
481 | return join(' OR ', @parts), @binds; | ||||
482 | } | ||||
483 | |||||
484 | return $self->_recurse_where($cond); | ||||
485 | } | ||||
486 | |||||
487 | # This is hideously ugly, but SQLA does not understand multicol IN expressions | ||||
488 | # FIXME TEMPORARY - DQ should have native syntax for this | ||||
489 | # moved here to raise API questions | ||||
490 | # | ||||
491 | # !!! EXPERIMENTAL API !!! WILL CHANGE !!! | ||||
492 | sub _where_op_multicolumn_in { | ||||
493 | my ($self, $lhs, $rhs) = @_; | ||||
494 | |||||
495 | if (! ref $lhs or ref $lhs eq 'ARRAY') { | ||||
496 | my (@sql, @bind); | ||||
497 | for (ref $lhs ? @$lhs : $lhs) { | ||||
498 | if (! ref $_) { | ||||
499 | push @sql, $self->_quote($_); | ||||
500 | } | ||||
501 | elsif (ref $_ eq 'SCALAR') { | ||||
502 | push @sql, $$_; | ||||
503 | } | ||||
504 | elsif (ref $_ eq 'REF' and ref $$_ eq 'ARRAY') { | ||||
505 | my ($s, @b) = @$$_; | ||||
506 | push @sql, $s; | ||||
507 | push @bind, @b; | ||||
508 | } | ||||
509 | else { | ||||
510 | $self->throw_exception("ARRAY of @{[ ref $_ ]}es unsupported for multicolumn IN lhs..."); | ||||
511 | } | ||||
512 | } | ||||
513 | $lhs = \[ join(', ', @sql), @bind]; | ||||
514 | } | ||||
515 | elsif (ref $lhs eq 'SCALAR') { | ||||
516 | $lhs = \[ $$lhs ]; | ||||
517 | } | ||||
518 | elsif (ref $lhs eq 'REF' and ref $$lhs eq 'ARRAY' ) { | ||||
519 | # noop | ||||
520 | } | ||||
521 | else { | ||||
522 | $self->throw_exception( ref($lhs) . "es unsupported for multicolumn IN lhs..."); | ||||
523 | } | ||||
524 | |||||
525 | # is this proper...? | ||||
526 | $rhs = \[ $self->_recurse_where($rhs) ]; | ||||
527 | |||||
528 | for ($lhs, $rhs) { | ||||
529 | $$_->[0] = "( $$_->[0] )" | ||||
530 | unless $$_->[0] =~ /^ \s* \( .* \) \s* $/xs; | ||||
531 | } | ||||
532 | |||||
533 | \[ join( ' IN ', shift @$$lhs, shift @$$rhs ), @$$lhs, @$$rhs ]; | ||||
534 | } | ||||
535 | |||||
536 | =head1 FURTHER QUESTIONS? | ||||
537 | |||||
538 | Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>. | ||||
539 | |||||
540 | =head1 COPYRIGHT AND LICENSE | ||||
541 | |||||
542 | This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE> | ||||
543 | by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can | ||||
544 | redistribute it and/or modify it under the same terms as the | ||||
545 | L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>. | ||||
546 | |||||
547 | =cut | ||||
548 | |||||
549 | 1 | 118µs | 1; # spent 118µs making 1 call to B::Hooks::EndOfScope::XS::__ANON__ | ||
# spent 1.50ms within DBIx::Class::SQLMaker::CORE:match which was called 3796 times, avg 396ns/call:
# 2844 times (531µs+0s) by DBIx::Class::SQLMaker::_order_by at line 327, avg 187ns/call
# 951 times (970µs+0s) by DBIx::Class::SQLMaker::_from_chunk_to_sql at line 435, avg 1µs/call
# once (2µs+0s) by DBIx::Class::SQLMaker::select at line 130 |