| 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 | DBIx::Class::SQLMaker::_quote |
| 525 | 3 | 1 | 3.15ms | 10.3ms | DBIx::Class::SQLMaker::_recurse_fields (recurses: max depth 2, inclusive time 8.42ms) |
| 88 | 1 | 1 | 2.80ms | 58.7ms | DBIx::Class::SQLMaker::select |
| 176 | 2 | 1 | 2.14ms | 8.46ms | DBIx::Class::SQLMaker::_from_chunk_to_sql (recurses: max depth 1, inclusive time 1.90ms) |
| 88 | 1 | 1 | 1.71ms | 2.17ms | DBIx::Class::SQLMaker::_order_by |
| 88 | 1 | 1 | 624µs | 624µs | DBIx::Class::SQLMaker::_assemble_binds |
| 88 | 1 | 1 | 539µs | 9.00ms | DBIx::Class::SQLMaker::_gen_from_blocks |
| 88 | 1 | 1 | 473µs | 9.84ms | DBIx::Class::SQLMaker::_table |
| 1056 | 2 | 1 | 366µs | 366µs | DBIx::Class::SQLMaker::CORE:match (opcode) |
| 88 | 1 | 1 | 362µs | 9.36ms | DBIx::Class::SQLMaker::_recurse_from |
| 88 | 1 | 1 | 332µs | 332µs | DBIx::Class::SQLMaker::_parse_rs_attrs |
| 1 | 1 | 1 | 29µs | 38µs | DBIx::Class::SQLMaker::BEGIN@67 |
| 1 | 1 | 1 | 18µs | 35µs | DBIx::Class::SQLMaker::BEGIN@70 |
| 1 | 1 | 1 | 14µs | 21µs | DBIx::Class::SQLMaker::BEGIN@3 |
| 1 | 1 | 1 | 14µs | 3.60ms | DBIx::Class::SQLMaker::BEGIN@34 |
| 1 | 1 | 1 | 13µs | 19µs | DBIx::Class::SQLMaker::BEGIN@39 |
| 1 | 1 | 1 | 12µs | 37µs | DBIx::Class::SQLMaker::BEGIN@41 |
| 1 | 1 | 1 | 9µs | 50µs | DBIx::Class::SQLMaker::BEGIN@42 |
| 1 | 1 | 1 | 8µs | 115µs | DBIx::Class::SQLMaker::BEGIN@43 |
| 1 | 1 | 1 | 7µs | 11µs | DBIx::Class::SQLMaker::BEGIN@4 |
| 1 | 1 | 1 | 6µs | 6µs | DBIx::Class::SQLMaker::_quote_chars |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::__ANON__[:75] |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::__ANON__[:80] |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::_generate_join_clause |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::_join_condition |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::_lock_select |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::_quoting_enabled |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::_split_order_chunk |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::_where_op_NEST |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::_where_op_multicolumn_in |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::insert |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::SQLMaker::throw_exception |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::belch |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::puke |
| 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 |