Filename | /usr/share/perl5/DBIx/Class/SQLMaker.pm |
Statements | Executed 8545 statements in 21.1ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
701 | 6 | 2 | 7.95ms | 14.7ms | _quote | DBIx::Class::SQLMaker::
525 | 3 | 1 | 3.15ms | 10.3ms | _recurse_fields (recurses: max depth 2, inclusive time 8.42ms) | DBIx::Class::SQLMaker::
88 | 1 | 1 | 2.80ms | 58.7ms | select | DBIx::Class::SQLMaker::
176 | 2 | 1 | 2.14ms | 8.46ms | _from_chunk_to_sql (recurses: max depth 1, inclusive time 1.90ms) | DBIx::Class::SQLMaker::
88 | 1 | 1 | 1.71ms | 2.17ms | _order_by | DBIx::Class::SQLMaker::
88 | 1 | 1 | 624µs | 624µs | _assemble_binds | DBIx::Class::SQLMaker::
88 | 1 | 1 | 539µs | 9.00ms | _gen_from_blocks | DBIx::Class::SQLMaker::
88 | 1 | 1 | 473µs | 9.84ms | _table | DBIx::Class::SQLMaker::
1056 | 2 | 1 | 366µs | 366µs | CORE:match (opcode) | DBIx::Class::SQLMaker::
88 | 1 | 1 | 362µs | 9.36ms | _recurse_from | DBIx::Class::SQLMaker::
88 | 1 | 1 | 332µs | 332µs | _parse_rs_attrs | DBIx::Class::SQLMaker::
1 | 1 | 1 | 29µs | 38µs | BEGIN@67 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 18µs | 35µs | BEGIN@70 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 14µs | 21µs | BEGIN@3 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 14µs | 3.60ms | BEGIN@34 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 13µs | 19µs | BEGIN@39 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 12µs | 37µs | BEGIN@41 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 9µs | 50µs | BEGIN@42 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 8µs | 115µs | BEGIN@43 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 7µs | 11µs | BEGIN@4 | DBIx::Class::SQLMaker::
1 | 1 | 1 | 6µs | 6µs | _quote_chars | 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 | 34µs | 2 | 28µs | # spent 21µs (14+7) within DBIx::Class::SQLMaker::BEGIN@3 which was called:
# once (14µs+7µs) by base::import at line 3 # spent 21µs making 1 call to DBIx::Class::SQLMaker::BEGIN@3
# spent 7µs making 1 call to strict::import |
4 | 2 | 85µs | 2 | 15µ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 | 124µs | 1 | 0s | # spent 3.60ms (14µs+3.59) within DBIx::Class::SQLMaker::BEGIN@34 which was called:
# once (14µs+3.59ms) by base::import at line 38 # spent 3.59ms making 1 call to base::import, recursion: max depth 1, sum of overlapping time 3.59ms |
35 | DBIx::Class::SQLMaker::LimitDialects | ||||
36 | SQL::Abstract | ||||
37 | DBIx::Class | ||||
38 | 1 | 70µs | 1 | 3.60ms | /; # spent 3.60ms making 1 call to DBIx::Class::SQLMaker::BEGIN@34 |
39 | 2 | 40µs | 2 | 25µs | # spent 19µs (13+6) within DBIx::Class::SQLMaker::BEGIN@39 which was called:
# once (13µs+6µs) by base::import at line 39 # spent 19µs making 1 call to DBIx::Class::SQLMaker::BEGIN@39
# spent 6µs making 1 call to mro::import |
40 | |||||
41 | 2 | 56µs | 2 | 62µs | # spent 37µs (12+25) within DBIx::Class::SQLMaker::BEGIN@41 which was called:
# once (12µs+25µs) by base::import at line 41 # spent 37µs making 1 call to DBIx::Class::SQLMaker::BEGIN@41
# spent 25µs making 1 call to Exporter::import |
42 | 2 | 27µs | 2 | 92µs | # spent 50µs (9+42) within DBIx::Class::SQLMaker::BEGIN@42 which was called:
# once (9µs+42µs) by base::import at line 42 # spent 50µs making 1 call to DBIx::Class::SQLMaker::BEGIN@42
# spent 42µs making 1 call to DBIx::Class::Carp::import |
43 | 2 | 278µs | 2 | 223µs | # spent 115µs (8+108) within DBIx::Class::SQLMaker::BEGIN@43 which was called:
# once (8µs+108µs) by base::import at line 43 # spent 115µs making 1 call to DBIx::Class::SQLMaker::BEGIN@43
# spent 108µs making 1 call to namespace::clean::import |
44 | |||||
45 | 1 | 0s | 1 | 400ns | __PACKAGE__->mk_group_accessors (simple => qw/quote_char name_sep limit_dialect/); # spent 400ns 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 6µs within DBIx::Class::SQLMaker::_quote_chars which was called:
# once (6µ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 | 8µ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 38µs (29+9) within DBIx::Class::SQLMaker::BEGIN@67 which was called:
# once (29µ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 | 186µs | 2 | 52µs | # spent 35µs (18+17) within DBIx::Class::SQLMaker::BEGIN@70 which was called:
# once (18µs+17µs) by base::import at line 70 # spent 35µs making 1 call to DBIx::Class::SQLMaker::BEGIN@70
# spent 17µ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 | 22µs | 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 | 16µs | 1 | 4µs | }; # spent 4µs making 1 call to Sub::Name::subname |
81 | 1 | 2.46ms | 1 | 38µs | } # spent 38µ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 14.7ms (7.95+6.75) within DBIx::Class::SQLMaker::_quote which was called 701 times, avg 21µs/call:
# 436 times (2.90ms+4.20ms) by DBIx::Class::SQLMaker::_recurse_fields at line 238, avg 16µs/call
# 88 times (3.80ms+846µs) by DBIx::Class::SQLMaker::_from_chunk_to_sql at line 442, avg 53µs/call
# 88 times (617µs+816µs) by DBIx::Class::SQLMaker::_from_chunk_to_sql at line 423, avg 16µs/call
# 87 times (618µs+875µs) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:941] at line 937 of SQL/Abstract.pm, avg 17µs/call
# once (7µs+9µs) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:932] at line 931 of SQL/Abstract.pm
# once (6µs+9µs) by SQL::Abstract::_where_hashpair_UNDEF at line 1051 of SQL/Abstract.pm | ||||
102 | 701 | 6.48ms | 701 | 2.00ms | $_[0]->next::method( ( $_[0]{_dequalify_idents} and ! ref $_[1] ) # spent 2.00ms making 701 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 58.7ms (2.80+55.9) within DBIx::Class::SQLMaker::select which was called 88 times, avg 667µs/call:
# 88 times (2.80ms+55.9ms) by DBIx::Class::Storage::DBI::_gen_sql_bind at line 1649 of DBIx/Class/Storage/DBI.pm, avg 667µs/call | ||||
118 | 88 | 60µs | my ($self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_; | ||
119 | |||||
120 | |||||
121 | 88 | 337µs | 88 | 10.3ms | ($fields, @{$self->{select_bind}}) = $self->_recurse_fields($fields); # spent 10.3ms making 88 calls to DBIx::Class::SQLMaker::_recurse_fields, avg 117µs/call |
122 | |||||
123 | 88 | 31µ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 | 88 | 48µs | $offset ||= 0; | ||
128 | |||||
129 | 88 | 37µs | if (defined $limit) { | ||
130 | $self->throw_exception('A supplied limit must be a positive integer') | ||||
131 | if ( $limit =~ /\D/ or $limit <= 0 ); | ||||
132 | } | ||||
133 | elsif ($offset) { | ||||
134 | $limit = $self->__max_int; | ||||
135 | } | ||||
136 | |||||
137 | |||||
138 | 88 | 18µs | my ($sql, @bind); | ||
139 | 88 | 49µs | if ($limit) { | ||
140 | # this is legacy code-flow from SQLA::Limit, it is not set in stone | ||||
141 | |||||
142 | ($sql, @bind) = $self->next::method ($table, $fields, $where); | ||||
143 | |||||
144 | my $limiter; | ||||
145 | |||||
146 | if( $limiter = $self->can ('emulate_limit') ) { | ||||
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 | my $dialect = $self->limit_dialect | ||||
158 | or $self->throw_exception( "Unable to generate SQL-limit - no limit dialect specified on $self" ); | ||||
159 | |||||
160 | $limiter = $self->can ("_$dialect") | ||||
161 | or $self->throw_exception(__PACKAGE__ . " does not implement the requested dialect '$dialect'"); | ||||
162 | } | ||||
163 | |||||
164 | $sql = $self->$limiter ( | ||||
165 | $sql, | ||||
166 | { %{$rs_attrs||{}}, _selector_sql => $fields }, | ||||
167 | $limit, | ||||
168 | $offset | ||||
169 | ); | ||||
170 | } | ||||
171 | else { | ||||
172 | 88 | 611µs | 88 | 284µs | ($sql, @bind) = $self->next::method ($table, $fields, $where, $rs_attrs); # spent 284µs making 88 calls to next::method, avg 3µs/call |
173 | } | ||||
174 | |||||
175 | 88 | 130µs | push @{$self->{where_bind}}, @bind; | ||
176 | |||||
177 | # this *must* be called, otherwise extra binds will remain in the sql-maker | ||||
178 | 88 | 165µs | 88 | 624µs | my @all_bind = $self->_assemble_binds; # spent 624µs making 88 calls to DBIx::Class::SQLMaker::_assemble_binds, avg 7µs/call |
179 | |||||
180 | 88 | 35µs | $sql .= $self->_lock_select ($rs_attrs->{for}) | ||
181 | if $rs_attrs->{for}; | ||||
182 | |||||
183 | 88 | 270µs | return wantarray ? ($sql, @all_bind) : $sql; | ||
184 | } | ||||
185 | |||||
186 | # spent 624µs within DBIx::Class::SQLMaker::_assemble_binds which was called 88 times, avg 7µs/call:
# 88 times (624µs+0s) by DBIx::Class::SQLMaker::select at line 178, avg 7µs/call | ||||
187 | 88 | 39µs | my $self = shift; | ||
188 | 88 | 708µs | return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/pre_select select from where group having order limit/); | ||
189 | } | ||||
190 | |||||
191 | 1 | 0s | 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 10.3ms (3.15+7.11) within DBIx::Class::SQLMaker::_recurse_fields which was called 525 times, avg 20µs/call:
# 436 times (1.25ms+-1.25ms) by DBIx::Class::SQLMaker::_recurse_fields at line 244, avg 0s/call
# 88 times (1.89ms+8.37ms) by DBIx::Class::SQLMaker::select at line 121, avg 117µs/call
# once (16µs+-16µs) by DBIx::Class::SQLMaker::_recurse_fields at line 270 | ||||
236 | 525 | 149µs | my ($self, $fields) = @_; | ||
237 | 525 | 158µs | my $ref = ref $fields; | ||
238 | 525 | 1.05ms | 436 | 7.10ms | return $self->_quote($fields) unless $ref; # spent 7.10ms making 436 calls to DBIx::Class::SQLMaker::_quote, avg 16µs/call |
239 | 89 | 53µs | return $$fields if $ref eq 'SCALAR'; | ||
240 | |||||
241 | 89 | 39µs | if ($ref eq 'ARRAY') { | ||
242 | 88 | 15µs | my (@select, @bind); | ||
243 | 88 | 85µs | for my $field (@$fields) { | ||
244 | 436 | 609µs | 436 | 0s | my ($select, @new_bind) = $self->_recurse_fields($field); # spent 8.37ms making 436 calls to DBIx::Class::SQLMaker::_recurse_fields, avg 19µs/call, recursion: max depth 1, sum of overlapping time 8.37ms |
245 | 436 | 133µs | push @select, $select; | ||
246 | 436 | 227µs | push @bind, @new_bind; | ||
247 | } | ||||
248 | 88 | 318µ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 | 13µs | 1 | 0s | my ($rhs_sql, @rhs_bind) = $self->_recurse_fields($rhs); # spent 48µs making 1 call to DBIx::Class::SQLMaker::_recurse_fields, recursion: max depth 2, sum of overlapping time 48µs |
271 | 1 | 6µs | 1 | 4µs | my $select = sprintf ('%s( %s )%s', # spent 4µ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 332µs within DBIx::Class::SQLMaker::_parse_rs_attrs which was called 88 times, avg 4µs/call:
# 88 times (332µs+0s) by DBIx::Class::SQLMaker::_order_by at line 327, avg 4µs/call | ||||
299 | 88 | 34µs | my ($self, $arg) = @_; | ||
300 | |||||
301 | 88 | 43µs | my $sql = ''; | ||
302 | |||||
303 | 88 | 42µ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 | 88 | 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 | 88 | 45µs | if (defined $arg->{order_by}) { | ||
317 | $sql .= $self->_order_by ($arg->{order_by}); | ||||
318 | } | ||||
319 | |||||
320 | 88 | 204µs | return $sql; | ||
321 | } | ||||
322 | |||||
323 | # spent 2.17ms (1.71+462µs) within DBIx::Class::SQLMaker::_order_by which was called 88 times, avg 25µs/call:
# 88 times (1.71ms+462µs) by SQL::Abstract::where at line 479 of SQL/Abstract.pm, avg 25µs/call | ||||
324 | 88 | 33µs | my ($self, $arg) = @_; | ||
325 | |||||
326 | # check that we are not called in legacy mode (order_by as 4th argument) | ||||
327 | 880 | 1.75ms | 880 | 462µs | if (ref $arg eq 'HASH' and not grep { $_ =~ /^-(?:desc|asc)/i } keys %$arg ) { # spent 332µs making 88 calls to DBIx::Class::SQLMaker::_parse_rs_attrs, avg 4µs/call
# spent 130µs making 792 calls to DBIx::Class::SQLMaker::CORE:match, avg 164ns/call |
328 | return $self->_parse_rs_attrs ($arg); | ||||
329 | } | ||||
330 | else { | ||||
331 | my ($sql, @bind) = $self->next::method($arg); | ||||
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 9.84ms (473µs+9.36) within DBIx::Class::SQLMaker::_table which was called 88 times, avg 112µs/call:
# 88 times (473µs+9.36ms) by DBIx::Class::SQLMaker::select at line 430 of SQL/Abstract.pm, avg 112µs/call | ||||
350 | # optimized due to hotttnesss | ||||
351 | # my ($self, $from) = @_; | ||||
352 | 88 | 67µs | if (my $ref = ref $_[1] ) { | ||
353 | 88 | 373µs | 88 | 9.36ms | if ($ref eq 'ARRAY') { # spent 9.36ms making 88 calls to DBIx::Class::SQLMaker::_recurse_from, avg 106µ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 9.36ms (362µs+9.00) within DBIx::Class::SQLMaker::_recurse_from which was called 88 times, avg 106µs/call:
# 88 times (362µs+9.00ms) by DBIx::Class::SQLMaker::_table at line 353, avg 106µs/call | ||||
380 | 88 | 23µs | my $self = shift; | ||
381 | 88 | 349µs | 88 | 9.00ms | return join (' ', $self->_gen_from_blocks(@_) ); # spent 9.00ms making 88 calls to DBIx::Class::SQLMaker::_gen_from_blocks, avg 102µs/call |
382 | } | ||||
383 | |||||
384 | # spent 9.00ms (539µs+8.46) within DBIx::Class::SQLMaker::_gen_from_blocks which was called 88 times, avg 102µs/call:
# 88 times (539µs+8.46ms) by DBIx::Class::SQLMaker::_recurse_from at line 381, avg 102µs/call | ||||
385 | 88 | 57µs | my ($self, $from, @joins) = @_; | ||
386 | |||||
387 | 88 | 221µs | 88 | 8.46ms | my @fchunks = $self->_from_chunk_to_sql($from); # spent 8.46ms making 88 calls to DBIx::Class::SQLMaker::_from_chunk_to_sql, avg 96µs/call |
388 | |||||
389 | 88 | 78µ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 | 88 | 185µs | return @fchunks; | ||
417 | } | ||||
418 | |||||
419 | # spent 8.46ms (2.14+6.32) within DBIx::Class::SQLMaker::_from_chunk_to_sql which was called 176 times, avg 48µs/call:
# 88 times (1.68ms+6.78ms) by DBIx::Class::SQLMaker::_gen_from_blocks at line 387, avg 96µs/call
# 88 times (463µs+-463µs) by DBIx::Class::SQLMaker::_from_chunk_to_sql at line 442, avg 0s/call | ||||
420 | 176 | 58µs | my ($self, $fromspec) = @_; | ||
421 | |||||
422 | 176 | 528µs | return join (' ', do { | ||
423 | 176 | 272µs | 88 | 1.43ms | if (! ref $fromspec) { # spent 1.43ms making 88 calls to DBIx::Class::SQLMaker::_quote, avg 16µ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 | 264 | 666µs | 264 | 236µs | { $_ => $fromspec->{$_} } # spent 236µs making 264 calls to DBIx::Class::SQLMaker::CORE:match, avg 895ns/call |
436 | 88 | 385µs | ( grep { $_ !~ /^\-/ } keys %$fromspec ) | ||
437 | ); | ||||
438 | |||||
439 | 88 | 31µ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 | 88 | 291µs | 176 | 4.65ms | ($self->_from_chunk_to_sql($table), $self->_quote($as) ); # spent 4.65ms making 88 calls to DBIx::Class::SQLMaker::_quote, avg 53µs/call
# spent 1.90ms making 88 calls to DBIx::Class::SQLMaker::_from_chunk_to_sql, avg 22µs/call, recursion: max depth 1, sum of overlapping time 1.90ms |
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 | 0s | 1 | 222µs | 1; # spent 222µs making 1 call to B::Hooks::EndOfScope::XS::__ANON__ |
sub DBIx::Class::SQLMaker::CORE:match; # opcode |