| Filename | /usr/share/perl5/SQL/Abstract.pm |
| Statements | Executed 873043 statements in 2.33s |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 75000 | 3 | 1 | 459ms | 685ms | SQL::Abstract::_assert_pass_injection_guard |
| 3000 | 1 | 1 | 225ms | 975ms | SQL::Abstract::_where_hashpair_HASHREF |
| 114001 | 5 | 1 | 164ms | 164ms | SQL::Abstract::CORE:match (opcode) |
| 3000 | 1 | 1 | 131ms | 392ms | SQL::Abstract::_where_unary_op |
| 108001 | 4 | 1 | 101ms | 101ms | SQL::Abstract::CORE:regcomp (opcode) |
| 3000 | 1 | 1 | 86.8ms | 1.13s | SQL::Abstract::_where_HASHREF |
| 12000 | 1 | 1 | 80.1ms | 86.5ms | SQL::Abstract::_refkind |
| 6000 | 2 | 1 | 77.5ms | 605ms | SQL::Abstract::_SWITCH_refkind (recurses: max depth 1, inclusive time 99.8ms) |
| 12000 | 2 | 1 | 67.4ms | 154ms | SQL::Abstract::_try_refkind |
| 6000 | 2 | 1 | 52.5ms | 153ms | SQL::Abstract::_METHOD_FOR_refkind |
| 3000 | 1 | 1 | 51.5ms | 520ms | SQL::Abstract::__ANON__[:941] |
| 3000 | 1 | 1 | 44.1ms | 1.45s | SQL::Abstract::where |
| 3000 | 1 | 1 | 35.1ms | 1.26s | SQL::Abstract::_recurse_where |
| 3000 | 1 | 1 | 21.3ms | 45.3ms | SQL::Abstract::__ANON__[:668] |
| 12000 | 3 | 1 | 20.3ms | 20.3ms | SQL::Abstract::_sqlcase |
| 3000 | 1 | 1 | 16.6ms | 16.6ms | SQL::Abstract::_bindtype |
| 15000 | 5 | 1 | 11.6ms | 11.6ms | SQL::Abstract::CORE:subst (opcode) |
| 3000 | 1 | 1 | 11.4ms | 11.4ms | SQL::Abstract::_join_sql_clauses |
| 6000 | 2 | 1 | 11.2ms | 11.2ms | SQL::Abstract::_convert |
| 3000 | 1 | 1 | 8.69ms | 8.69ms | SQL::Abstract::_debug |
| 6000 | 2 | 1 | 2.93ms | 2.93ms | SQL::Abstract::CORE:sort (opcode) |
| 1 | 1 | 1 | 42µs | 64µs | SQL::Abstract::new |
| 16 | 7 | 1 | 16µs | 16µs | SQL::Abstract::CORE:qr (opcode) |
| 1 | 1 | 1 | 16µs | 23µs | SQL::Abstract::BEGIN@3 |
| 1 | 1 | 1 | 15µs | 23µs | SQL::Abstract::BEGIN@86 |
| 1 | 1 | 1 | 8µs | 15µs | SQL::Abstract::BEGIN@9 |
| 1 | 1 | 1 | 8µs | 8µs | SQL::Abstract::BEGIN@12 |
| 1 | 1 | 1 | 7µs | 12µs | SQL::Abstract::BEGIN@4 |
| 1 | 1 | 1 | 4µs | 4µs | SQL::Abstract::BEGIN@5 |
| 1 | 1 | 1 | 4µs | 4µs | SQL::Abstract::BEGIN@6 |
| 1 | 1 | 1 | 3µs | 3µs | SQL::Abstract::BEGIN@7 |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::AUTOLOAD |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::DESTROY |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1105] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1108] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1117] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1120] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1125] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1131] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1134] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1144] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1147] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1174] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1177] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1182] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1188] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1196] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1215] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1220] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1226] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1230] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1234] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1281] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1282] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1304] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1310] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1312] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1314] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1316] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1337] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1340] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1349] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1362] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1363] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1364] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1554] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1559] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1561] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:1564] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:236] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:237] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:238] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:309] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:316] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:324] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:328] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:333] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:378] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:384] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:387] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:399] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:403] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:532] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:538] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:540] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:542] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:547] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:549] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:601] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:635] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:637] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:671] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:688] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:694] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:702] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:710] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:714] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:718] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:731] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:735] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:739] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:751] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:755] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:759] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:891] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:911] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:920] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:932] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:959] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::__ANON__[:962] |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_assert_bindval_matches_bindtype |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_insert_ARRAYREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_insert_ARRAYREFREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_insert_HASHREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_insert_SCALARREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_insert_returning |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_insert_values |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_open_outer_paren |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_order_by |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_order_by_chunks |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_quote |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_table |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_ARRAYREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_ARRAYREFREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_SCALAR |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_SCALARREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_UNDEF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_field_BETWEEN |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_field_IN |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_field_IS |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_field_op_ARRAYREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_hashpair_ARRAYREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_hashpair_ARRAYREFREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_hashpair_SCALAR |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_hashpair_SCALARREF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_hashpair_UNDEF |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_op_ANDOR |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_op_BOOL |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_op_IDENT |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_op_NEST |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::_where_op_VALUE |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::delete |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::generate |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::insert |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::is_literal_value |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::is_plain_value |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::select |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::update |
| 0 | 0 | 0 | 0s | 0s | SQL::Abstract::values |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package SQL::Abstract; # see doc at end of file | ||||
| 2 | |||||
| 3 | 2 | 37µs | 2 | 31µs | # spent 23µs (16+7) within SQL::Abstract::BEGIN@3 which was called:
# once (16µs+7µs) by DBIx::Class::Row::BEGIN@12 at line 3 # spent 23µs making 1 call to SQL::Abstract::BEGIN@3
# spent 7µs making 1 call to strict::import |
| 4 | 2 | 27µs | 2 | 16µs | # spent 12µs (7+4) within SQL::Abstract::BEGIN@4 which was called:
# once (7µs+4µs) by DBIx::Class::Row::BEGIN@12 at line 4 # spent 12µs making 1 call to SQL::Abstract::BEGIN@4
# spent 4µs making 1 call to warnings::import |
| 5 | 2 | 20µs | 1 | 4µs | # spent 4µs within SQL::Abstract::BEGIN@5 which was called:
# once (4µs+0s) by DBIx::Class::Row::BEGIN@12 at line 5 # spent 4µs making 1 call to SQL::Abstract::BEGIN@5 |
| 6 | 2 | 18µs | 1 | 4µs | # spent 4µs within SQL::Abstract::BEGIN@6 which was called:
# once (4µs+0s) by DBIx::Class::Row::BEGIN@12 at line 6 # spent 4µs making 1 call to SQL::Abstract::BEGIN@6 |
| 7 | 2 | 25µs | 1 | 3µs | # spent 3µs within SQL::Abstract::BEGIN@7 which was called:
# once (3µs+0s) by DBIx::Class::Row::BEGIN@12 at line 7 # spent 3µs making 1 call to SQL::Abstract::BEGIN@7 |
| 8 | |||||
| 9 | 2 | 123µs | 2 | 21µs | # spent 15µs (8+6) within SQL::Abstract::BEGIN@9 which was called:
# once (8µs+6µs) by DBIx::Class::Row::BEGIN@12 at line 9 # spent 15µs making 1 call to SQL::Abstract::BEGIN@9
# spent 6µs making 1 call to Exporter::import |
| 10 | 1 | 2µs | our @EXPORT_OK = qw(is_plain_value is_literal_value); | ||
| 11 | |||||
| 12 | # spent 8µs within SQL::Abstract::BEGIN@12 which was called:
# once (8µs+0s) by DBIx::Class::Row::BEGIN@12 at line 24 | ||||
| 13 | 1 | 2µs | if ($] < 5.009_005) { | ||
| 14 | require MRO::Compat; | ||||
| 15 | } | ||||
| 16 | else { | ||||
| 17 | 1 | 700ns | require mro; | ||
| 18 | } | ||||
| 19 | |||||
| 20 | *SQL::Abstract::_ENV_::DETECT_AUTOGENERATED_STRINGIFICATION = $ENV{SQLA_ISVALUE_IGNORE_AUTOGENERATED_STRINGIFICATION} | ||||
| 21 | ? sub () { 0 } | ||||
| 22 | : sub () { 1 } | ||||
| 23 | 1 | 7µs | ; | ||
| 24 | 1 | 472µs | 1 | 8µs | } # spent 8µs making 1 call to SQL::Abstract::BEGIN@12 |
| 25 | |||||
| 26 | #====================================================================== | ||||
| 27 | # GLOBALS | ||||
| 28 | #====================================================================== | ||||
| 29 | |||||
| 30 | 1 | 300ns | our $VERSION = '1.81'; | ||
| 31 | |||||
| 32 | # This would confuse some packagers | ||||
| 33 | 1 | 18µs | 1 | 4µs | $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases # spent 4µs making 1 call to SQL::Abstract::CORE:match |
| 34 | |||||
| 35 | 1 | 100ns | our $AUTOLOAD; | ||
| 36 | |||||
| 37 | # special operators (-in, -between). May be extended/overridden by user. | ||||
| 38 | # See section WHERE: BUILTIN SPECIAL OPERATORS below for implementation | ||||
| 39 | 1 | 20µs | 5 | 7µs | my @BUILTIN_SPECIAL_OPS = ( # spent 7µs making 5 calls to SQL::Abstract::CORE:qr, avg 1µs/call |
| 40 | {regex => qr/^ (?: not \s )? between $/ix, handler => '_where_field_BETWEEN'}, | ||||
| 41 | {regex => qr/^ (?: not \s )? in $/ix, handler => '_where_field_IN'}, | ||||
| 42 | {regex => qr/^ ident $/ix, handler => '_where_op_IDENT'}, | ||||
| 43 | {regex => qr/^ value $/ix, handler => '_where_op_VALUE'}, | ||||
| 44 | {regex => qr/^ is (?: \s+ not )? $/ix, handler => '_where_field_IS'}, | ||||
| 45 | ); | ||||
| 46 | |||||
| 47 | # unaryish operators - key maps to handler | ||||
| 48 | 1 | 26µs | 6 | 4µs | my @BUILTIN_UNARY_OPS = ( # spent 4µs making 6 calls to SQL::Abstract::CORE:qr, avg 683ns/call |
| 49 | # the digits are backcompat stuff | ||||
| 50 | { regex => qr/^ and (?: [_\s]? \d+ )? $/xi, handler => '_where_op_ANDOR' }, | ||||
| 51 | { regex => qr/^ or (?: [_\s]? \d+ )? $/xi, handler => '_where_op_ANDOR' }, | ||||
| 52 | { regex => qr/^ nest (?: [_\s]? \d+ )? $/xi, handler => '_where_op_NEST' }, | ||||
| 53 | { regex => qr/^ (?: not \s )? bool $/xi, handler => '_where_op_BOOL' }, | ||||
| 54 | { regex => qr/^ ident $/xi, handler => '_where_op_IDENT' }, | ||||
| 55 | { regex => qr/^ value $/xi, handler => '_where_op_VALUE' }, | ||||
| 56 | ); | ||||
| 57 | |||||
| 58 | #====================================================================== | ||||
| 59 | # DEBUGGING AND ERROR REPORTING | ||||
| 60 | #====================================================================== | ||||
| 61 | |||||
| 62 | # spent 8.69ms within SQL::Abstract::_debug which was called 3000 times, avg 3µs/call:
# 3000 times (8.69ms+0s) by SQL::Abstract::_where_unary_op at line 655, avg 3µs/call | ||||
| 63 | 3000 | 9.53ms | return unless $_[0]->{debug}; shift; # a little faster | ||
| 64 | my $func = (caller(1))[3]; | ||||
| 65 | warn "[$func] ", @_, "\n"; | ||||
| 66 | } | ||||
| 67 | |||||
| 68 | sub belch (@) { | ||||
| 69 | my($func) = (caller(1))[3]; | ||||
| 70 | Carp::carp "[$func] Warning: ", @_; | ||||
| 71 | } | ||||
| 72 | |||||
| 73 | sub puke (@) { | ||||
| 74 | my($func) = (caller(1))[3]; | ||||
| 75 | Carp::croak "[$func] Fatal: ", @_; | ||||
| 76 | } | ||||
| 77 | |||||
| 78 | sub is_literal_value ($) { | ||||
| 79 | ref $_[0] eq 'SCALAR' ? [ ${$_[0]} ] | ||||
| 80 | : ( ref $_[0] eq 'REF' and ref ${$_[0]} eq 'ARRAY' ) ? [ @${ $_[0] } ] | ||||
| 81 | : undef; | ||||
| 82 | } | ||||
| 83 | |||||
| 84 | # FIXME XSify - this can be done so much more efficiently | ||||
| 85 | sub is_plain_value ($) { | ||||
| 86 | 2 | 8.23ms | 2 | 30µs | # spent 23µs (15+8) within SQL::Abstract::BEGIN@86 which was called:
# once (15µs+8µs) by DBIx::Class::Row::BEGIN@12 at line 86 # spent 23µs making 1 call to SQL::Abstract::BEGIN@86
# spent 8µs making 1 call to strict::unimport |
| 87 | ! length ref $_[0] ? \($_[0]) | ||||
| 88 | : ( | ||||
| 89 | ref $_[0] eq 'HASH' and keys %{$_[0]} == 1 | ||||
| 90 | and | ||||
| 91 | exists $_[0]->{-value} | ||||
| 92 | ) ? \($_[0]->{-value}) | ||||
| 93 | : ( | ||||
| 94 | # reuse @_ for even moar speedz | ||||
| 95 | defined ( $_[1] = Scalar::Util::blessed $_[0] ) | ||||
| 96 | and | ||||
| 97 | ( | ||||
| 98 | |||||
| - - | |||||
| 101 | # simply using ->can('(""') can leave behind stub methods that | ||||
| 102 | # break actually using the overload later (see L<perldiag/Stub | ||||
| 103 | # found while resolving method "%s" overloading "%s" in package | ||||
| 104 | # "%s"> and the source of overload::mycan()) | ||||
| 105 | # | ||||
| 106 | # either has stringification which DBI SHOULD prefer out of the box | ||||
| 107 | grep { *{ (qq[${_}::(""]) }{CODE} } @{ $_[2] = mro::get_linear_isa( $_[1] ) } | ||||
| 108 | or | ||||
| 109 | ( | ||||
| 110 | |||||
| 111 | SQL::Abstract::_ENV_::DETECT_AUTOGENERATED_STRINGIFICATION | ||||
| 112 | and | ||||
| 113 | ( | ||||
| 114 | grep { *{"${_}::(0+"}{CODE} } @{$_[2]} | ||||
| 115 | or | ||||
| 116 | grep { *{"${_}::(bool"}{CODE} } @{$_[2]} | ||||
| 117 | ) | ||||
| 118 | and | ||||
| 119 | ( | ||||
| 120 | # no fallback specified at all | ||||
| 121 | ! ( ($_[3]) = grep { *{"${_}::()"}{CODE} } @{$_[2]} ) | ||||
| 122 | or | ||||
| 123 | ! defined ${"$_[3]::()"} | ||||
| 124 | |||||
| 125 | or | ||||
| 126 | !! ${"$_[3]::()"} | ||||
| 127 | |||||
| 128 | ) | ||||
| 129 | ) | ||||
| 130 | ) | ||||
| 131 | ) ? \($_[0]) | ||||
| 132 | : undef; | ||||
| 133 | } | ||||
| 134 | |||||
| - - | |||||
| 137 | #====================================================================== | ||||
| 138 | # NEW | ||||
| 139 | #====================================================================== | ||||
| 140 | |||||
| 141 | # spent 64µs (42+22) within SQL::Abstract::new which was called:
# once (42µs+22µs) by DBIx::Class::Storage::DBI::sql_maker at line 1021 of DBIx/Class/Storage/DBI.pm | ||||
| 142 | 1 | 500ns | my $self = shift; | ||
| 143 | 1 | 400ns | my $class = ref($self) || $self; | ||
| 144 | 1 | 3µs | my %opt = (ref $_[0] eq 'HASH') ? %{$_[0]} : @_; | ||
| 145 | |||||
| 146 | # choose our case by keeping an option around | ||||
| 147 | 1 | 400ns | delete $opt{case} if $opt{case} && $opt{case} ne 'lower'; | ||
| 148 | |||||
| 149 | # default logic for interpreting arrayrefs | ||||
| 150 | 1 | 1µs | $opt{logic} = $opt{logic} ? uc $opt{logic} : 'OR'; | ||
| 151 | |||||
| 152 | # how to return bind vars | ||||
| 153 | 1 | 400ns | $opt{bindtype} ||= 'normal'; | ||
| 154 | |||||
| 155 | # default comparison is "=", but can be overridden | ||||
| 156 | 1 | 2µs | $opt{cmp} ||= '='; | ||
| 157 | |||||
| 158 | # try to recognize which are the 'equality' and 'inequality' ops | ||||
| 159 | # (temporary quickfix (in 2007), should go through a more seasoned API) | ||||
| 160 | 1 | 31µs | 2 | 18µs | $opt{equality_op} = qr/^( \Q$opt{cmp}\E | \= )$/ix; # spent 16µs making 1 call to SQL::Abstract::CORE:regcomp
# spent 2µs making 1 call to SQL::Abstract::CORE:qr |
| 161 | 1 | 4µs | 1 | 1µs | $opt{inequality_op} = qr/^( != | <> )$/ix; # spent 1µs making 1 call to SQL::Abstract::CORE:qr |
| 162 | |||||
| 163 | 1 | 3µs | 1 | 1µs | $opt{like_op} = qr/^ (is\s+)? r?like $/xi; # spent 1µs making 1 call to SQL::Abstract::CORE:qr |
| 164 | 1 | 4µs | 1 | 1µs | $opt{not_like_op} = qr/^ (is\s+)? not \s+ r?like $/xi; # spent 1µs making 1 call to SQL::Abstract::CORE:qr |
| 165 | |||||
| 166 | # SQL booleans | ||||
| 167 | 1 | 600ns | $opt{sqltrue} ||= '1=1'; | ||
| 168 | 1 | 800ns | $opt{sqlfalse} ||= '0=1'; | ||
| 169 | |||||
| 170 | # special operators | ||||
| 171 | 1 | 1µs | $opt{special_ops} ||= []; | ||
| 172 | # regexes are applied in order, thus push after user-defines | ||||
| 173 | 1 | 2µs | push @{$opt{special_ops}}, @BUILTIN_SPECIAL_OPS; | ||
| 174 | |||||
| 175 | # unary operators | ||||
| 176 | 1 | 500ns | $opt{unary_ops} ||= []; | ||
| 177 | 1 | 2µs | push @{$opt{unary_ops}}, @BUILTIN_UNARY_OPS; | ||
| 178 | |||||
| 179 | # rudimentary sanity-check for user supplied bits treated as functions/operators | ||||
| 180 | # If a purported function matches this regular expression, an exception is thrown. | ||||
| 181 | # Literal SQL is *NOT* subject to this check, only functions (and column names | ||||
| 182 | # when quoting is not in effect) | ||||
| 183 | |||||
| 184 | # FIXME | ||||
| 185 | # need to guard against ()'s in column names too, but this will break tons of | ||||
| 186 | # hacks... ideas anyone? | ||||
| 187 | 1 | 4µs | 1 | 800ns | $opt{injection_guard} ||= qr/ # spent 800ns making 1 call to SQL::Abstract::CORE:qr |
| 188 | \; | ||||
| 189 | | | ||||
| 190 | ^ \s* go \s | ||||
| 191 | /xmi; | ||||
| 192 | |||||
| 193 | 1 | 5µs | return bless \%opt, $class; | ||
| 194 | } | ||||
| 195 | |||||
| 196 | |||||
| 197 | # spent 685ms (459+226) within SQL::Abstract::_assert_pass_injection_guard which was called 75000 times, avg 9µs/call:
# 69000 times (421ms+212ms) by DBIx::Class::SQLMaker::_quote at line 1381, avg 9µs/call
# 3000 times (27.1ms+6.19ms) by SQL::Abstract::_where_hashpair_HASHREF at line 871, avg 11µs/call
# 3000 times (10.7ms+7.12ms) by SQL::Abstract::_where_unary_op at line 657, avg 6µs/call | ||||
| 198 | 75000 | 887ms | 150000 | 226ms | if ($_[1] =~ $_[0]->{injection_guard}) { # spent 157ms making 75000 calls to SQL::Abstract::CORE:match, avg 2µs/call
# spent 69.0ms making 75000 calls to SQL::Abstract::CORE:regcomp, avg 920ns/call |
| 199 | my $class = ref $_[0]; | ||||
| 200 | puke "Possible SQL injection attempt '$_[1]'. If this is indeed a part of the " | ||||
| 201 | . "desired SQL use literal SQL ( \'...' or \[ '...' ] ) or supply your own " | ||||
| 202 | . "{injection_guard} attribute to ${class}->new()" | ||||
| 203 | } | ||||
| 204 | } | ||||
| 205 | |||||
| 206 | |||||
| 207 | #====================================================================== | ||||
| 208 | # INSERT methods | ||||
| 209 | #====================================================================== | ||||
| 210 | |||||
| 211 | sub insert { | ||||
| 212 | my $self = shift; | ||||
| 213 | my $table = $self->_table(shift); | ||||
| 214 | my $data = shift || return; | ||||
| 215 | my $options = shift; | ||||
| 216 | |||||
| 217 | my $method = $self->_METHOD_FOR_refkind("_insert", $data); | ||||
| 218 | my ($sql, @bind) = $self->$method($data); | ||||
| 219 | $sql = join " ", $self->_sqlcase('insert into'), $table, $sql; | ||||
| 220 | |||||
| 221 | if ($options->{returning}) { | ||||
| 222 | my ($s, @b) = $self->_insert_returning ($options); | ||||
| 223 | $sql .= $s; | ||||
| 224 | push @bind, @b; | ||||
| 225 | } | ||||
| 226 | |||||
| 227 | return wantarray ? ($sql, @bind) : $sql; | ||||
| 228 | } | ||||
| 229 | |||||
| 230 | sub _insert_returning { | ||||
| 231 | my ($self, $options) = @_; | ||||
| 232 | |||||
| 233 | my $f = $options->{returning}; | ||||
| 234 | |||||
| 235 | my $fieldlist = $self->_SWITCH_refkind($f, { | ||||
| 236 | ARRAYREF => sub {join ', ', map { $self->_quote($_) } @$f;}, | ||||
| 237 | SCALAR => sub {$self->_quote($f)}, | ||||
| 238 | SCALARREF => sub {$$f}, | ||||
| 239 | }); | ||||
| 240 | return $self->_sqlcase(' returning ') . $fieldlist; | ||||
| 241 | } | ||||
| 242 | |||||
| 243 | sub _insert_HASHREF { # explicit list of fields and then values | ||||
| 244 | my ($self, $data) = @_; | ||||
| 245 | |||||
| 246 | my @fields = sort keys %$data; | ||||
| 247 | |||||
| 248 | my ($sql, @bind) = $self->_insert_values($data); | ||||
| 249 | |||||
| 250 | # assemble SQL | ||||
| 251 | $_ = $self->_quote($_) foreach @fields; | ||||
| 252 | $sql = "( ".join(", ", @fields).") ".$sql; | ||||
| 253 | |||||
| 254 | return ($sql, @bind); | ||||
| 255 | } | ||||
| 256 | |||||
| 257 | sub _insert_ARRAYREF { # just generate values(?,?) part (no list of fields) | ||||
| 258 | my ($self, $data) = @_; | ||||
| 259 | |||||
| 260 | # no names (arrayref) so can't generate bindtype | ||||
| 261 | $self->{bindtype} ne 'columns' | ||||
| 262 | or belch "can't do 'columns' bindtype when called with arrayref"; | ||||
| 263 | |||||
| 264 | # fold the list of values into a hash of column name - value pairs | ||||
| 265 | # (where the column names are artificially generated, and their | ||||
| 266 | # lexicographical ordering keep the ordering of the original list) | ||||
| 267 | my $i = "a"; # incremented values will be in lexicographical order | ||||
| 268 | my $data_in_hash = { map { ($i++ => $_) } @$data }; | ||||
| 269 | |||||
| 270 | return $self->_insert_values($data_in_hash); | ||||
| 271 | } | ||||
| 272 | |||||
| 273 | sub _insert_ARRAYREFREF { # literal SQL with bind | ||||
| 274 | my ($self, $data) = @_; | ||||
| 275 | |||||
| 276 | my ($sql, @bind) = @${$data}; | ||||
| 277 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 278 | |||||
| 279 | return ($sql, @bind); | ||||
| 280 | } | ||||
| 281 | |||||
| 282 | |||||
| 283 | sub _insert_SCALARREF { # literal SQL without bind | ||||
| 284 | my ($self, $data) = @_; | ||||
| 285 | |||||
| 286 | return ($$data); | ||||
| 287 | } | ||||
| 288 | |||||
| 289 | sub _insert_values { | ||||
| 290 | my ($self, $data) = @_; | ||||
| 291 | |||||
| 292 | my (@values, @all_bind); | ||||
| 293 | foreach my $column (sort keys %$data) { | ||||
| 294 | my $v = $data->{$column}; | ||||
| 295 | |||||
| 296 | $self->_SWITCH_refkind($v, { | ||||
| 297 | |||||
| 298 | ARRAYREF => sub { | ||||
| 299 | if ($self->{array_datatypes}) { # if array datatype are activated | ||||
| 300 | push @values, '?'; | ||||
| 301 | push @all_bind, $self->_bindtype($column, $v); | ||||
| 302 | } | ||||
| 303 | else { # else literal SQL with bind | ||||
| 304 | my ($sql, @bind) = @$v; | ||||
| 305 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 306 | push @values, $sql; | ||||
| 307 | push @all_bind, @bind; | ||||
| 308 | } | ||||
| 309 | }, | ||||
| 310 | |||||
| 311 | ARRAYREFREF => sub { # literal SQL with bind | ||||
| 312 | my ($sql, @bind) = @${$v}; | ||||
| 313 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 314 | push @values, $sql; | ||||
| 315 | push @all_bind, @bind; | ||||
| 316 | }, | ||||
| 317 | |||||
| 318 | # THINK : anything useful to do with a HASHREF ? | ||||
| 319 | HASHREF => sub { # (nothing, but old SQLA passed it through) | ||||
| 320 | #TODO in SQLA >= 2.0 it will die instead | ||||
| 321 | belch "HASH ref as bind value in insert is not supported"; | ||||
| 322 | push @values, '?'; | ||||
| 323 | push @all_bind, $self->_bindtype($column, $v); | ||||
| 324 | }, | ||||
| 325 | |||||
| 326 | SCALARREF => sub { # literal SQL without bind | ||||
| 327 | push @values, $$v; | ||||
| 328 | }, | ||||
| 329 | |||||
| 330 | SCALAR_or_UNDEF => sub { | ||||
| 331 | push @values, '?'; | ||||
| 332 | push @all_bind, $self->_bindtype($column, $v); | ||||
| 333 | }, | ||||
| 334 | |||||
| 335 | }); | ||||
| 336 | |||||
| 337 | } | ||||
| 338 | |||||
| 339 | my $sql = $self->_sqlcase('values')." ( ".join(", ", @values)." )"; | ||||
| 340 | return ($sql, @all_bind); | ||||
| 341 | } | ||||
| 342 | |||||
| - - | |||||
| 345 | #====================================================================== | ||||
| 346 | # UPDATE methods | ||||
| 347 | #====================================================================== | ||||
| 348 | |||||
| 349 | |||||
| 350 | sub update { | ||||
| 351 | my $self = shift; | ||||
| 352 | my $table = $self->_table(shift); | ||||
| 353 | my $data = shift || return; | ||||
| 354 | my $where = shift; | ||||
| 355 | |||||
| 356 | # first build the 'SET' part of the sql statement | ||||
| 357 | my (@set, @all_bind); | ||||
| 358 | puke "Unsupported data type specified to \$sql->update" | ||||
| 359 | unless ref $data eq 'HASH'; | ||||
| 360 | |||||
| 361 | for my $k (sort keys %$data) { | ||||
| 362 | my $v = $data->{$k}; | ||||
| 363 | my $r = ref $v; | ||||
| 364 | my $label = $self->_quote($k); | ||||
| 365 | |||||
| 366 | $self->_SWITCH_refkind($v, { | ||||
| 367 | ARRAYREF => sub { | ||||
| 368 | if ($self->{array_datatypes}) { # array datatype | ||||
| 369 | push @set, "$label = ?"; | ||||
| 370 | push @all_bind, $self->_bindtype($k, $v); | ||||
| 371 | } | ||||
| 372 | else { # literal SQL with bind | ||||
| 373 | my ($sql, @bind) = @$v; | ||||
| 374 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 375 | push @set, "$label = $sql"; | ||||
| 376 | push @all_bind, @bind; | ||||
| 377 | } | ||||
| 378 | }, | ||||
| 379 | ARRAYREFREF => sub { # literal SQL with bind | ||||
| 380 | my ($sql, @bind) = @${$v}; | ||||
| 381 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 382 | push @set, "$label = $sql"; | ||||
| 383 | push @all_bind, @bind; | ||||
| 384 | }, | ||||
| 385 | SCALARREF => sub { # literal SQL without bind | ||||
| 386 | push @set, "$label = $$v"; | ||||
| 387 | }, | ||||
| 388 | HASHREF => sub { | ||||
| 389 | my ($op, $arg, @rest) = %$v; | ||||
| 390 | |||||
| 391 | puke 'Operator calls in update must be in the form { -op => $arg }' | ||||
| 392 | if (@rest or not $op =~ /^\-(.+)/); | ||||
| 393 | |||||
| 394 | local $self->{_nested_func_lhs} = $k; | ||||
| 395 | my ($sql, @bind) = $self->_where_unary_op ($1, $arg); | ||||
| 396 | |||||
| 397 | push @set, "$label = $sql"; | ||||
| 398 | push @all_bind, @bind; | ||||
| 399 | }, | ||||
| 400 | SCALAR_or_UNDEF => sub { | ||||
| 401 | push @set, "$label = ?"; | ||||
| 402 | push @all_bind, $self->_bindtype($k, $v); | ||||
| 403 | }, | ||||
| 404 | }); | ||||
| 405 | } | ||||
| 406 | |||||
| 407 | # generate sql | ||||
| 408 | my $sql = $self->_sqlcase('update') . " $table " . $self->_sqlcase('set ') | ||||
| 409 | . join ', ', @set; | ||||
| 410 | |||||
| 411 | if ($where) { | ||||
| 412 | my($where_sql, @where_bind) = $self->where($where); | ||||
| 413 | $sql .= $where_sql; | ||||
| 414 | push @all_bind, @where_bind; | ||||
| 415 | } | ||||
| 416 | |||||
| 417 | return wantarray ? ($sql, @all_bind) : $sql; | ||||
| 418 | } | ||||
| 419 | |||||
| - - | |||||
| 423 | #====================================================================== | ||||
| 424 | # SELECT | ||||
| 425 | #====================================================================== | ||||
| 426 | |||||
| 427 | |||||
| 428 | sub select { | ||||
| 429 | 3000 | 1.60ms | my $self = shift; | ||
| 430 | 3000 | 8.51ms | 3000 | 300ms | my $table = $self->_table(shift); # spent 300ms making 3000 calls to DBIx::Class::SQLMaker::_table, avg 100µs/call |
| 431 | 3000 | 1.70ms | my $fields = shift || '*'; | ||
| 432 | 3000 | 964µs | my $where = shift; | ||
| 433 | 3000 | 928µs | my $order = shift; | ||
| 434 | |||||
| 435 | 3000 | 9.40ms | 3000 | 1.45s | my($where_sql, @bind) = $self->where($where, $order); # spent 1.45s making 3000 calls to SQL::Abstract::where, avg 484µs/call |
| 436 | |||||
| 437 | 3000 | 1.84ms | my $f = (ref $fields eq 'ARRAY') ? join ', ', map { $self->_quote($_) } @$fields | ||
| 438 | : $fields; | ||||
| 439 | 3000 | 17.2ms | 6000 | 7.70ms | my $sql = join(' ', $self->_sqlcase('select'), $f, # spent 7.70ms making 6000 calls to SQL::Abstract::_sqlcase, avg 1µs/call |
| 440 | $self->_sqlcase('from'), $table) | ||||
| 441 | . $where_sql; | ||||
| 442 | |||||
| 443 | 3000 | 3.76ms | return wantarray ? ($sql, @bind) : $sql; | ||
| 444 | } | ||||
| 445 | |||||
| 446 | #====================================================================== | ||||
| 447 | # DELETE | ||||
| 448 | #====================================================================== | ||||
| 449 | |||||
| 450 | |||||
| 451 | sub delete { | ||||
| 452 | my $self = shift; | ||||
| 453 | my $table = $self->_table(shift); | ||||
| 454 | my $where = shift; | ||||
| 455 | |||||
| 456 | |||||
| 457 | my($where_sql, @bind) = $self->where($where); | ||||
| 458 | my $sql = $self->_sqlcase('delete from') . " $table" . $where_sql; | ||||
| 459 | |||||
| 460 | return wantarray ? ($sql, @bind) : $sql; | ||||
| 461 | } | ||||
| 462 | |||||
| 463 | |||||
| 464 | #====================================================================== | ||||
| 465 | # WHERE: entry point | ||||
| 466 | #====================================================================== | ||||
| 467 | |||||
| - - | |||||
| 470 | # Finally, a separate routine just to handle WHERE clauses | ||||
| 471 | # spent 1.45s (44.1ms+1.41) within SQL::Abstract::where which was called 3000 times, avg 484µs/call:
# 3000 times (44.1ms+1.41s) by DBIx::Class::SQLMaker::select at line 435, avg 484µs/call | ||||
| 472 | 3000 | 1.54ms | my ($self, $where, $order) = @_; | ||
| 473 | |||||
| 474 | # where ? | ||||
| 475 | 3000 | 7.05ms | 3000 | 1.26s | my ($sql, @bind) = $self->_recurse_where($where); # spent 1.26s making 3000 calls to SQL::Abstract::_recurse_where, avg 422µs/call |
| 476 | 3000 | 6.78ms | 3000 | 4.19ms | $sql = $sql ? $self->_sqlcase(' where ') . "( $sql )" : ''; # spent 4.19ms making 3000 calls to SQL::Abstract::_sqlcase, avg 1µs/call |
| 477 | |||||
| 478 | # order by? | ||||
| 479 | 3000 | 9.15ms | 3000 | 138ms | if ($order) { # spent 138ms making 3000 calls to DBIx::Class::SQLMaker::_order_by, avg 46µs/call |
| 480 | $sql .= $self->_order_by($order); | ||||
| 481 | } | ||||
| 482 | |||||
| 483 | 3000 | 14.6ms | return wantarray ? ($sql, @bind) : $sql; | ||
| 484 | } | ||||
| 485 | |||||
| 486 | |||||
| 487 | # spent 1.26s (35.1ms+1.23) within SQL::Abstract::_recurse_where which was called 3000 times, avg 422µs/call:
# 3000 times (35.1ms+1.23s) by SQL::Abstract::where at line 475, avg 422µs/call | ||||
| 488 | 3000 | 1.36ms | my ($self, $where, $logic) = @_; | ||
| 489 | |||||
| 490 | # dispatch on appropriate method according to refkind of $where | ||||
| 491 | 3000 | 7.03ms | 3000 | 96.3ms | my $method = $self->_METHOD_FOR_refkind("_where", $where); # spent 96.3ms making 3000 calls to SQL::Abstract::_METHOD_FOR_refkind, avg 32µs/call |
| 492 | |||||
| 493 | 3000 | 8.77ms | 3000 | 1.13s | my ($sql, @bind) = $self->$method($where, $logic); # spent 1.13s making 3000 calls to SQL::Abstract::_where_HASHREF, avg 378µs/call |
| 494 | |||||
| 495 | # DBIx::Class used to call _recurse_where in scalar context | ||||
| 496 | # something else might too... | ||||
| 497 | 3000 | 13.6ms | if (wantarray) { | ||
| 498 | return ($sql, @bind); | ||||
| 499 | } | ||||
| 500 | else { | ||||
| 501 | belch "Calling _recurse_where in scalar context is deprecated and will go away before 2.0"; | ||||
| 502 | return $sql; | ||||
| 503 | } | ||||
| 504 | } | ||||
| 505 | |||||
| - - | |||||
| 508 | #====================================================================== | ||||
| 509 | # WHERE: top-level ARRAYREF | ||||
| 510 | #====================================================================== | ||||
| 511 | |||||
| 512 | |||||
| 513 | sub _where_ARRAYREF { | ||||
| 514 | my ($self, $where, $logic) = @_; | ||||
| 515 | |||||
| 516 | $logic = uc($logic || $self->{logic}); | ||||
| 517 | $logic eq 'AND' or $logic eq 'OR' or puke "unknown logic: $logic"; | ||||
| 518 | |||||
| 519 | my @clauses = @$where; | ||||
| 520 | |||||
| 521 | my (@sql_clauses, @all_bind); | ||||
| 522 | # need to use while() so can shift() for pairs | ||||
| 523 | while (@clauses) { | ||||
| 524 | my $el = shift @clauses; | ||||
| 525 | |||||
| 526 | $el = undef if (defined $el and ! length $el); | ||||
| 527 | |||||
| 528 | # switch according to kind of $el and get corresponding ($sql, @bind) | ||||
| 529 | my ($sql, @bind) = $self->_SWITCH_refkind($el, { | ||||
| 530 | |||||
| 531 | # skip empty elements, otherwise get invalid trailing AND stuff | ||||
| 532 | ARRAYREF => sub {$self->_recurse_where($el) if @$el}, | ||||
| 533 | |||||
| 534 | ARRAYREFREF => sub { | ||||
| 535 | my ($s, @b) = @$$el; | ||||
| 536 | $self->_assert_bindval_matches_bindtype(@b); | ||||
| 537 | ($s, @b); | ||||
| 538 | }, | ||||
| 539 | |||||
| 540 | HASHREF => sub {$self->_recurse_where($el, 'and') if %$el}, | ||||
| 541 | |||||
| 542 | SCALARREF => sub { ($$el); }, | ||||
| 543 | |||||
| 544 | SCALAR => sub { | ||||
| 545 | # top-level arrayref with scalars, recurse in pairs | ||||
| 546 | $self->_recurse_where({$el => shift(@clauses)}) | ||||
| 547 | }, | ||||
| 548 | |||||
| 549 | UNDEF => sub {puke "Supplying an empty left hand side argument is not supported in array-pairs" }, | ||||
| 550 | }); | ||||
| 551 | |||||
| 552 | if ($sql) { | ||||
| 553 | push @sql_clauses, $sql; | ||||
| 554 | push @all_bind, @bind; | ||||
| 555 | } | ||||
| 556 | } | ||||
| 557 | |||||
| 558 | return $self->_join_sql_clauses($logic, \@sql_clauses, \@all_bind); | ||||
| 559 | } | ||||
| 560 | |||||
| 561 | #====================================================================== | ||||
| 562 | # WHERE: top-level ARRAYREFREF | ||||
| 563 | #====================================================================== | ||||
| 564 | |||||
| 565 | sub _where_ARRAYREFREF { | ||||
| 566 | my ($self, $where) = @_; | ||||
| 567 | my ($sql, @bind) = @$$where; | ||||
| 568 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 569 | return ($sql, @bind); | ||||
| 570 | } | ||||
| 571 | |||||
| 572 | #====================================================================== | ||||
| 573 | # WHERE: top-level HASHREF | ||||
| 574 | #====================================================================== | ||||
| 575 | |||||
| 576 | # spent 1.13s (86.8ms+1.05) within SQL::Abstract::_where_HASHREF which was called 3000 times, avg 378µs/call:
# 3000 times (86.8ms+1.05s) by SQL::Abstract::_recurse_where at line 493, avg 378µs/call | ||||
| 577 | 3000 | 1.72ms | my ($self, $where) = @_; | ||
| 578 | 3000 | 946µs | my (@sql_clauses, @all_bind); | ||
| 579 | |||||
| 580 | 3000 | 21.6ms | 3000 | 2.04ms | for my $k (sort keys %$where) { # spent 2.04ms making 3000 calls to SQL::Abstract::CORE:sort, avg 682ns/call |
| 581 | 3000 | 1.62ms | my $v = $where->{$k}; | ||
| 582 | |||||
| 583 | # ($k => $v) is either a special unary op or a regular hashpair | ||||
| 584 | 3000 | 2.66ms | my ($sql, @bind) = do { | ||
| 585 | 3000 | 13.7ms | 3000 | 1.79ms | if ($k =~ /^-./) { # spent 1.79ms making 3000 calls to SQL::Abstract::CORE:match, avg 595ns/call |
| 586 | # put the operator in canonical form | ||||
| 587 | my $op = $k; | ||||
| 588 | $op = substr $op, 1; # remove initial dash | ||||
| 589 | $op =~ s/^\s+|\s+$//g;# remove leading/trailing space | ||||
| 590 | $op =~ s/\s+/ /g; # compress whitespace | ||||
| 591 | |||||
| 592 | # so that -not_foo works correctly | ||||
| 593 | $op =~ s/^not_/NOT /i; | ||||
| 594 | |||||
| 595 | $self->_debug("Unary OP(-$op) within hashref, recursing..."); | ||||
| 596 | my ($s, @b) = $self->_where_unary_op ($op, $v); | ||||
| 597 | |||||
| 598 | # top level vs nested | ||||
| 599 | # we assume that handled unary ops will take care of their ()s | ||||
| 600 | $s = "($s)" unless ( | ||||
| 601 | List::Util::first {$op =~ $_->{regex}} @{$self->{unary_ops}} | ||||
| 602 | or | ||||
| 603 | ( defined $self->{_nested_func_lhs} and $self->{_nested_func_lhs} eq $k ) | ||||
| 604 | ); | ||||
| 605 | ($s, @b); | ||||
| 606 | } | ||||
| 607 | else { | ||||
| 608 | 3000 | 2.05ms | if (! length $k) { | ||
| 609 | if (is_literal_value ($v) ) { | ||||
| 610 | belch 'Hash-pairs consisting of an empty string with a literal are deprecated, and will be removed in 2.0: use -and => [ $literal ] instead'; | ||||
| 611 | } | ||||
| 612 | else { | ||||
| 613 | puke "Supplying an empty left hand side argument is not supported in hash-pairs"; | ||||
| 614 | } | ||||
| 615 | } | ||||
| 616 | |||||
| 617 | 3000 | 4.36ms | 3000 | 56.9ms | my $method = $self->_METHOD_FOR_refkind("_where_hashpair", $v); # spent 56.9ms making 3000 calls to SQL::Abstract::_METHOD_FOR_refkind, avg 19µs/call |
| 618 | 3000 | 6.97ms | 3000 | 975ms | $self->$method($k, $v); # spent 975ms making 3000 calls to SQL::Abstract::_where_hashpair_HASHREF, avg 325µs/call |
| 619 | } | ||||
| 620 | }; | ||||
| 621 | |||||
| 622 | 3000 | 1.28ms | push @sql_clauses, $sql; | ||
| 623 | 3000 | 2.81ms | push @all_bind, @bind; | ||
| 624 | } | ||||
| 625 | |||||
| 626 | 3000 | 17.5ms | 3000 | 11.4ms | return $self->_join_sql_clauses('and', \@sql_clauses, \@all_bind); # spent 11.4ms making 3000 calls to SQL::Abstract::_join_sql_clauses, avg 4µs/call |
| 627 | } | ||||
| 628 | |||||
| 629 | # spent 392ms (131+261) within SQL::Abstract::_where_unary_op which was called 3000 times, avg 131µs/call:
# 3000 times (131ms+261ms) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:941] at line 935, avg 131µs/call | ||||
| 630 | 3000 | 1.96ms | my ($self, $op, $rhs) = @_; | ||
| 631 | |||||
| 632 | # top level special ops are illegal in general | ||||
| 633 | # this includes the -ident/-value ops (dual purpose unary and special) | ||||
| 634 | puke "Illegal use of top-level '-$op'" | ||||
| 635 | 3000 | 3.52ms | if ! defined $self->{_nested_func_lhs} and List::Util::first {$op =~ $_->{regex}} @{$self->{special_ops}}; | ||
| 636 | |||||
| 637 | 21000 | 156ms | 39000 | 146ms | if (my $op_entry = List::Util::first {$op =~ $_->{regex}} @{$self->{unary_ops}}) { # spent 126ms making 3000 calls to List::Util::first, avg 42µs/call
# spent 16.6ms making 18000 calls to SQL::Abstract::CORE:regcomp, avg 922ns/call
# spent 3.13ms making 18000 calls to SQL::Abstract::CORE:match, avg 174ns/call |
| 638 | my $handler = $op_entry->{handler}; | ||||
| 639 | |||||
| 640 | if (not ref $handler) { | ||||
| 641 | if ($op =~ s/ [_\s]? \d+ $//x ) { | ||||
| 642 | belch 'Use of [and|or|nest]_N modifiers is deprecated and will be removed in SQLA v2.0. ' | ||||
| 643 | . "You probably wanted ...-and => [ -$op => COND1, -$op => COND2 ... ]"; | ||||
| 644 | } | ||||
| 645 | return $self->$handler ($op, $rhs); | ||||
| 646 | } | ||||
| 647 | elsif (ref $handler eq 'CODE') { | ||||
| 648 | return $handler->($self, $op, $rhs); | ||||
| 649 | } | ||||
| 650 | else { | ||||
| 651 | puke "Illegal handler for operator $op - expecting a method name or a coderef"; | ||||
| 652 | } | ||||
| 653 | } | ||||
| 654 | |||||
| 655 | 3000 | 10.2ms | 3000 | 8.69ms | $self->_debug("Generic unary OP: $op - recursing as function"); # spent 8.69ms making 3000 calls to SQL::Abstract::_debug, avg 3µs/call |
| 656 | |||||
| 657 | 3000 | 4.58ms | 3000 | 17.8ms | $self->_assert_pass_injection_guard($op); # spent 17.8ms making 3000 calls to SQL::Abstract::_assert_pass_injection_guard, avg 6µs/call |
| 658 | |||||
| 659 | my ($sql, @bind) = $self->_SWITCH_refkind ($rhs, { | ||||
| 660 | # spent 45.3ms (21.3+24.0) within SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:668] which was called 3000 times, avg 15µs/call:
# 3000 times (21.3ms+24.0ms) by SQL::Abstract::_SWITCH_refkind at line 1521, avg 15µs/call | ||||
| 661 | 3000 | 1.59ms | puke "Illegal use of top-level '-$op'" | ||
| 662 | unless defined $self->{_nested_func_lhs}; | ||||
| 663 | |||||
| 664 | return ( | ||||
| 665 | 3000 | 23.8ms | 6000 | 24.0ms | $self->_convert('?'), # spent 16.6ms making 3000 calls to SQL::Abstract::_bindtype, avg 6µs/call
# spent 7.45ms making 3000 calls to SQL::Abstract::_convert, avg 2µs/call |
| 666 | $self->_bindtype($self->{_nested_func_lhs}, $rhs) | ||||
| 667 | ); | ||||
| 668 | }, | ||||
| 669 | FALLBACK => sub { | ||||
| 670 | $self->_recurse_where ($rhs) | ||||
| 671 | }, | ||||
| 672 | 3000 | 34.8ms | 3000 | 0s | }); # spent 99.8ms making 3000 calls to SQL::Abstract::_SWITCH_refkind, avg 33µs/call, recursion: max depth 1, sum of overlapping time 99.8ms |
| 673 | |||||
| 674 | 3000 | 16.0ms | 3000 | 8.40ms | $sql = sprintf ('%s %s', # spent 8.40ms making 3000 calls to SQL::Abstract::_sqlcase, avg 3µs/call |
| 675 | $self->_sqlcase($op), | ||||
| 676 | $sql, | ||||
| 677 | ); | ||||
| 678 | |||||
| 679 | 3000 | 21.2ms | return ($sql, @bind); | ||
| 680 | } | ||||
| 681 | |||||
| 682 | sub _where_op_ANDOR { | ||||
| 683 | my ($self, $op, $v) = @_; | ||||
| 684 | |||||
| 685 | $self->_SWITCH_refkind($v, { | ||||
| 686 | ARRAYREF => sub { | ||||
| 687 | return $self->_where_ARRAYREF($v, $op); | ||||
| 688 | }, | ||||
| 689 | |||||
| 690 | HASHREF => sub { | ||||
| 691 | return ( $op =~ /^or/i ) | ||||
| 692 | ? $self->_where_ARRAYREF( [ map { $_ => $v->{$_} } ( sort keys %$v ) ], $op ) | ||||
| 693 | : $self->_where_HASHREF($v); | ||||
| 694 | }, | ||||
| 695 | |||||
| 696 | SCALARREF => sub { | ||||
| 697 | puke "-$op => \\\$scalar makes little sense, use " . | ||||
| 698 | ($op =~ /^or/i | ||||
| 699 | ? '[ \$scalar, \%rest_of_conditions ] instead' | ||||
| 700 | : '-and => [ \$scalar, \%rest_of_conditions ] instead' | ||||
| 701 | ); | ||||
| 702 | }, | ||||
| 703 | |||||
| 704 | ARRAYREFREF => sub { | ||||
| 705 | puke "-$op => \\[...] makes little sense, use " . | ||||
| 706 | ($op =~ /^or/i | ||||
| 707 | ? '[ \[...], \%rest_of_conditions ] instead' | ||||
| 708 | : '-and => [ \[...], \%rest_of_conditions ] instead' | ||||
| 709 | ); | ||||
| 710 | }, | ||||
| 711 | |||||
| 712 | SCALAR => sub { # permissively interpreted as SQL | ||||
| 713 | puke "-$op => \$value makes little sense, use -bool => \$value instead"; | ||||
| 714 | }, | ||||
| 715 | |||||
| 716 | UNDEF => sub { | ||||
| 717 | puke "-$op => undef not supported"; | ||||
| 718 | }, | ||||
| 719 | }); | ||||
| 720 | } | ||||
| 721 | |||||
| 722 | sub _where_op_NEST { | ||||
| 723 | my ($self, $op, $v) = @_; | ||||
| 724 | |||||
| 725 | $self->_SWITCH_refkind($v, { | ||||
| 726 | |||||
| 727 | SCALAR => sub { # permissively interpreted as SQL | ||||
| 728 | belch "literal SQL should be -nest => \\'scalar' " | ||||
| 729 | . "instead of -nest => 'scalar' "; | ||||
| 730 | return ($v); | ||||
| 731 | }, | ||||
| 732 | |||||
| 733 | UNDEF => sub { | ||||
| 734 | puke "-$op => undef not supported"; | ||||
| 735 | }, | ||||
| 736 | |||||
| 737 | FALLBACK => sub { | ||||
| 738 | $self->_recurse_where ($v); | ||||
| 739 | }, | ||||
| 740 | |||||
| 741 | }); | ||||
| 742 | } | ||||
| 743 | |||||
| 744 | |||||
| 745 | sub _where_op_BOOL { | ||||
| 746 | my ($self, $op, $v) = @_; | ||||
| 747 | |||||
| 748 | my ($s, @b) = $self->_SWITCH_refkind($v, { | ||||
| 749 | SCALAR => sub { # interpreted as SQL column | ||||
| 750 | $self->_convert($self->_quote($v)); | ||||
| 751 | }, | ||||
| 752 | |||||
| 753 | UNDEF => sub { | ||||
| 754 | puke "-$op => undef not supported"; | ||||
| 755 | }, | ||||
| 756 | |||||
| 757 | FALLBACK => sub { | ||||
| 758 | $self->_recurse_where ($v); | ||||
| 759 | }, | ||||
| 760 | }); | ||||
| 761 | |||||
| 762 | $s = "(NOT $s)" if $op =~ /^not/i; | ||||
| 763 | ($s, @b); | ||||
| 764 | } | ||||
| 765 | |||||
| 766 | |||||
| 767 | sub _where_op_IDENT { | ||||
| 768 | my $self = shift; | ||||
| 769 | my ($op, $rhs) = splice @_, -2; | ||||
| 770 | if (! defined $rhs or length ref $rhs) { | ||||
| 771 | puke "-$op requires a single plain scalar argument (a quotable identifier)"; | ||||
| 772 | } | ||||
| 773 | |||||
| 774 | # in case we are called as a top level special op (no '=') | ||||
| 775 | my $lhs = shift; | ||||
| 776 | |||||
| 777 | $_ = $self->_convert($self->_quote($_)) for ($lhs, $rhs); | ||||
| 778 | |||||
| 779 | return $lhs | ||||
| 780 | ? "$lhs = $rhs" | ||||
| 781 | : $rhs | ||||
| 782 | ; | ||||
| 783 | } | ||||
| 784 | |||||
| 785 | sub _where_op_VALUE { | ||||
| 786 | my $self = shift; | ||||
| 787 | my ($op, $rhs) = splice @_, -2; | ||||
| 788 | |||||
| 789 | # in case we are called as a top level special op (no '=') | ||||
| 790 | my $lhs = shift; | ||||
| 791 | |||||
| 792 | # special-case NULL | ||||
| 793 | if (! defined $rhs) { | ||||
| 794 | return defined $lhs | ||||
| 795 | ? $self->_convert($self->_quote($lhs)) . ' IS NULL' | ||||
| 796 | : undef | ||||
| 797 | ; | ||||
| 798 | } | ||||
| 799 | |||||
| 800 | my @bind = | ||||
| 801 | $self->_bindtype ( | ||||
| 802 | ( defined $lhs ? $lhs : $self->{_nested_func_lhs} ), | ||||
| 803 | $rhs, | ||||
| 804 | ) | ||||
| 805 | ; | ||||
| 806 | |||||
| 807 | return $lhs | ||||
| 808 | ? ( | ||||
| 809 | $self->_convert($self->_quote($lhs)) . ' = ' . $self->_convert('?'), | ||||
| 810 | @bind | ||||
| 811 | ) | ||||
| 812 | : ( | ||||
| 813 | $self->_convert('?'), | ||||
| 814 | @bind, | ||||
| 815 | ) | ||||
| 816 | ; | ||||
| 817 | } | ||||
| 818 | |||||
| 819 | sub _where_hashpair_ARRAYREF { | ||||
| 820 | my ($self, $k, $v) = @_; | ||||
| 821 | |||||
| 822 | if( @$v ) { | ||||
| 823 | my @v = @$v; # need copy because of shift below | ||||
| 824 | $self->_debug("ARRAY($k) means distribute over elements"); | ||||
| 825 | |||||
| 826 | # put apart first element if it is an operator (-and, -or) | ||||
| 827 | my $op = ( | ||||
| 828 | (defined $v[0] && $v[0] =~ /^ - (?: AND|OR ) $/ix) | ||||
| 829 | ? shift @v | ||||
| 830 | : '' | ||||
| 831 | ); | ||||
| 832 | my @distributed = map { {$k => $_} } @v; | ||||
| 833 | |||||
| 834 | if ($op) { | ||||
| 835 | $self->_debug("OP($op) reinjected into the distributed array"); | ||||
| 836 | unshift @distributed, $op; | ||||
| 837 | } | ||||
| 838 | |||||
| 839 | my $logic = $op ? substr($op, 1) : ''; | ||||
| 840 | |||||
| 841 | return $self->_recurse_where(\@distributed, $logic); | ||||
| 842 | } | ||||
| 843 | else { | ||||
| 844 | $self->_debug("empty ARRAY($k) means 0=1"); | ||||
| 845 | return ($self->{sqlfalse}); | ||||
| 846 | } | ||||
| 847 | } | ||||
| 848 | |||||
| 849 | # spent 975ms (225+750) within SQL::Abstract::_where_hashpair_HASHREF which was called 3000 times, avg 325µs/call:
# 3000 times (225ms+750ms) by SQL::Abstract::_where_HASHREF at line 618, avg 325µs/call | ||||
| 850 | 3000 | 1.64ms | my ($self, $k, $v, $logic) = @_; | ||
| 851 | 3000 | 1.96ms | $logic ||= 'and'; | ||
| 852 | |||||
| 853 | 3000 | 5.28ms | local $self->{_nested_func_lhs} = defined $self->{_nested_func_lhs} | ||
| 854 | ? $self->{_nested_func_lhs} | ||||
| 855 | : $k | ||||
| 856 | ; | ||||
| 857 | |||||
| 858 | 3000 | 594µs | my ($all_sql, @all_bind); | ||
| 859 | |||||
| 860 | 3000 | 20.0ms | 3000 | 888µs | for my $orig_op (sort keys %$v) { # spent 888µs making 3000 calls to SQL::Abstract::CORE:sort, avg 296ns/call |
| 861 | 3000 | 1.63ms | my $val = $v->{$orig_op}; | ||
| 862 | |||||
| 863 | # put the operator in canonical form | ||||
| 864 | 3000 | 838µs | my $op = $orig_op; | ||
| 865 | |||||
| 866 | # FIXME - we need to phase out dash-less ops | ||||
| 867 | 3000 | 9.53ms | 3000 | 3.01ms | $op =~ s/^-//; # remove possible initial dash # spent 3.01ms making 3000 calls to SQL::Abstract::CORE:subst, avg 1µs/call |
| 868 | 3000 | 13.5ms | 3000 | 4.12ms | $op =~ s/^\s+|\s+$//g;# remove leading/trailing space # spent 4.12ms making 3000 calls to SQL::Abstract::CORE:subst, avg 1µs/call |
| 869 | 3000 | 17.7ms | 3000 | 3.03ms | $op =~ s/\s+/ /g; # compress whitespace # spent 3.03ms making 3000 calls to SQL::Abstract::CORE:subst, avg 1µs/call |
| 870 | |||||
| 871 | 3000 | 4.81ms | 3000 | 33.3ms | $self->_assert_pass_injection_guard($op); # spent 33.3ms making 3000 calls to SQL::Abstract::_assert_pass_injection_guard, avg 11µs/call |
| 872 | |||||
| 873 | # fixup is_not | ||||
| 874 | 3000 | 5.80ms | 3000 | 759µs | $op =~ s/^is_not/IS NOT/i; # spent 759µs making 3000 calls to SQL::Abstract::CORE:subst, avg 253ns/call |
| 875 | |||||
| 876 | # so that -not_foo works correctly | ||||
| 877 | 3000 | 14.0ms | 3000 | 674µs | $op =~ s/^not_/NOT /i; # spent 674µs making 3000 calls to SQL::Abstract::CORE:subst, avg 225ns/call |
| 878 | |||||
| 879 | # another retarded special case: foo => { $op => { -value => undef } } | ||||
| 880 | 3000 | 1.31ms | if (ref $val eq 'HASH' and keys %$val == 1 and exists $val->{-value} and ! defined $val->{-value} ) { | ||
| 881 | $val = undef; | ||||
| 882 | } | ||||
| 883 | |||||
| 884 | 3000 | 723µs | my ($sql, @bind); | ||
| 885 | |||||
| 886 | # CASE: col-value logic modifiers | ||||
| 887 | 18000 | 168ms | 36000 | 117ms | if ( $orig_op =~ /^ \- (and|or) $/xi ) { # spent 98.5ms making 3000 calls to List::Util::first, avg 33µs/call
# spent 15.4ms making 15000 calls to SQL::Abstract::CORE:regcomp, avg 1µs/call
# spent 2.69ms making 18000 calls to SQL::Abstract::CORE:match, avg 150ns/call |
| 888 | ($sql, @bind) = $self->_where_hashpair_HASHREF($k, $val, $1); | ||||
| 889 | } | ||||
| 890 | # CASE: special operators like -in or -between | ||||
| 891 | elsif ( my $special_op = List::Util::first {$op =~ $_->{regex}} @{$self->{special_ops}} ) { | ||||
| 892 | my $handler = $special_op->{handler}; | ||||
| 893 | if (! $handler) { | ||||
| 894 | puke "No handler supplied for special operator $orig_op"; | ||||
| 895 | } | ||||
| 896 | elsif (not ref $handler) { | ||||
| 897 | ($sql, @bind) = $self->$handler ($k, $op, $val); | ||||
| 898 | } | ||||
| 899 | elsif (ref $handler eq 'CODE') { | ||||
| 900 | ($sql, @bind) = $handler->($self, $k, $op, $val); | ||||
| 901 | } | ||||
| 902 | else { | ||||
| 903 | puke "Illegal handler for special operator $orig_op - expecting a method name or a coderef"; | ||||
| 904 | } | ||||
| 905 | } | ||||
| 906 | else { | ||||
| 907 | $self->_SWITCH_refkind($val, { | ||||
| 908 | |||||
| 909 | ARRAYREF => sub { # CASE: col => {op => \@vals} | ||||
| 910 | ($sql, @bind) = $self->_where_field_op_ARRAYREF($k, $op, $val); | ||||
| 911 | }, | ||||
| 912 | |||||
| 913 | ARRAYREFREF => sub { # CASE: col => {op => \[$sql, @bind]} (literal SQL with bind) | ||||
| 914 | my ($sub_sql, @sub_bind) = @$$val; | ||||
| 915 | $self->_assert_bindval_matches_bindtype(@sub_bind); | ||||
| 916 | $sql = join ' ', $self->_convert($self->_quote($k)), | ||||
| 917 | $self->_sqlcase($op), | ||||
| 918 | $sub_sql; | ||||
| 919 | @bind = @sub_bind; | ||||
| 920 | }, | ||||
| 921 | |||||
| 922 | UNDEF => sub { # CASE: col => {op => undef} : sql "IS (NOT)? NULL" | ||||
| 923 | my $is = | ||||
| 924 | $op =~ /^not$/i ? 'is not' # legacy | ||||
| 925 | : $op =~ $self->{equality_op} ? 'is' | ||||
| 926 | : $op =~ $self->{like_op} ? belch("Supplying an undefined argument to '@{[ uc $op]}' is deprecated") && 'is' | ||||
| 927 | : $op =~ $self->{inequality_op} ? 'is not' | ||||
| 928 | : $op =~ $self->{not_like_op} ? belch("Supplying an undefined argument to '@{[ uc $op]}' is deprecated") && 'is not' | ||||
| 929 | : puke "unexpected operator '$orig_op' with undef operand"; | ||||
| 930 | |||||
| 931 | $sql = $self->_quote($k) . $self->_sqlcase(" $is null"); | ||||
| 932 | }, | ||||
| 933 | |||||
| 934 | # spent 520ms (51.5+468) within SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:941] which was called 3000 times, avg 173µs/call:
# 3000 times (51.5ms+468ms) by SQL::Abstract::_SWITCH_refkind at line 1521, avg 173µs/call | ||||
| 935 | 3000 | 11.9ms | 3000 | 392ms | ($sql, @bind) = $self->_where_unary_op ($op, $val); # spent 392ms making 3000 calls to SQL::Abstract::_where_unary_op, avg 131µs/call |
| 936 | |||||
| 937 | 3000 | 33.1ms | 6000 | 76.2ms | $sql = join (' ', # spent 72.4ms making 3000 calls to DBIx::Class::SQLMaker::_quote, avg 24µs/call
# spent 3.76ms making 3000 calls to SQL::Abstract::_convert, avg 1µs/call |
| 938 | $self->_convert($self->_quote($k)), | ||||
| 939 | $self->{_nested_func_lhs} eq $k ? $sql : "($sql)", # top level vs nested | ||||
| 940 | ); | ||||
| 941 | }, | ||||
| 942 | 3000 | 50.6ms | 3000 | 605ms | }); # spent 605ms making 3000 calls to SQL::Abstract::_SWITCH_refkind, avg 202µs/call |
| 943 | } | ||||
| 944 | |||||
| 945 | 3000 | 3.11ms | ($all_sql) = (defined $all_sql and $all_sql) ? $self->_join_sql_clauses($logic, [$all_sql, $sql], []) : $sql; | ||
| 946 | 3000 | 5.91ms | push @all_bind, @bind; | ||
| 947 | } | ||||
| 948 | 3000 | 15.3ms | return ($all_sql, @all_bind); | ||
| 949 | } | ||||
| 950 | |||||
| 951 | sub _where_field_IS { | ||||
| 952 | my ($self, $k, $op, $v) = @_; | ||||
| 953 | |||||
| 954 | my ($s) = $self->_SWITCH_refkind($v, { | ||||
| 955 | UNDEF => sub { | ||||
| 956 | join ' ', | ||||
| 957 | $self->_convert($self->_quote($k)), | ||||
| 958 | map { $self->_sqlcase($_)} ($op, 'null') | ||||
| 959 | }, | ||||
| 960 | FALLBACK => sub { | ||||
| 961 | puke "$op can only take undef as argument"; | ||||
| 962 | }, | ||||
| 963 | }); | ||||
| 964 | |||||
| 965 | $s; | ||||
| 966 | } | ||||
| 967 | |||||
| 968 | sub _where_field_op_ARRAYREF { | ||||
| 969 | my ($self, $k, $op, $vals) = @_; | ||||
| 970 | |||||
| 971 | my @vals = @$vals; #always work on a copy | ||||
| 972 | |||||
| 973 | if(@vals) { | ||||
| 974 | $self->_debug(sprintf '%s means multiple elements: [ %s ]', | ||||
| 975 | $vals, | ||||
| 976 | join (', ', map { defined $_ ? "'$_'" : 'NULL' } @vals ), | ||||
| 977 | ); | ||||
| 978 | |||||
| 979 | # see if the first element is an -and/-or op | ||||
| 980 | my $logic; | ||||
| 981 | if (defined $vals[0] && $vals[0] =~ /^ - ( AND|OR ) $/ix) { | ||||
| 982 | $logic = uc $1; | ||||
| 983 | shift @vals; | ||||
| 984 | } | ||||
| 985 | |||||
| 986 | # a long standing API wart - an attempt to change this behavior during | ||||
| 987 | # the 1.50 series failed *spectacularly*. Warn instead and leave the | ||||
| 988 | # behavior as is | ||||
| 989 | if ( | ||||
| 990 | @vals > 1 | ||||
| 991 | and | ||||
| 992 | (!$logic or $logic eq 'OR') | ||||
| 993 | and | ||||
| 994 | ( $op =~ $self->{inequality_op} or $op =~ $self->{not_like_op} ) | ||||
| 995 | ) { | ||||
| 996 | my $o = uc($op); | ||||
| 997 | belch "A multi-element arrayref as an argument to the inequality op '$o' " | ||||
| 998 | . 'is technically equivalent to an always-true 1=1 (you probably wanted ' | ||||
| 999 | . "to say ...{ \$inequality_op => [ -and => \@values ] }... instead)" | ||||
| 1000 | ; | ||||
| 1001 | } | ||||
| 1002 | |||||
| 1003 | # distribute $op over each remaining member of @vals, append logic if exists | ||||
| 1004 | return $self->_recurse_where([map { {$k => {$op, $_}} } @vals], $logic); | ||||
| 1005 | |||||
| 1006 | } | ||||
| 1007 | else { | ||||
| 1008 | # try to DWIM on equality operators | ||||
| 1009 | return | ||||
| 1010 | $op =~ $self->{equality_op} ? $self->{sqlfalse} | ||||
| 1011 | : $op =~ $self->{like_op} ? belch("Supplying an empty arrayref to '@{[ uc $op]}' is deprecated") && $self->{sqlfalse} | ||||
| 1012 | : $op =~ $self->{inequality_op} ? $self->{sqltrue} | ||||
| 1013 | : $op =~ $self->{not_like_op} ? belch("Supplying an empty arrayref to '@{[ uc $op]}' is deprecated") && $self->{sqltrue} | ||||
| 1014 | : puke "operator '$op' applied on an empty array (field '$k')"; | ||||
| 1015 | } | ||||
| 1016 | } | ||||
| 1017 | |||||
| 1018 | |||||
| 1019 | sub _where_hashpair_SCALARREF { | ||||
| 1020 | my ($self, $k, $v) = @_; | ||||
| 1021 | $self->_debug("SCALAR($k) means literal SQL: $$v"); | ||||
| 1022 | my $sql = $self->_quote($k) . " " . $$v; | ||||
| 1023 | return ($sql); | ||||
| 1024 | } | ||||
| 1025 | |||||
| 1026 | # literal SQL with bind | ||||
| 1027 | sub _where_hashpair_ARRAYREFREF { | ||||
| 1028 | my ($self, $k, $v) = @_; | ||||
| 1029 | $self->_debug("REF($k) means literal SQL: @${$v}"); | ||||
| 1030 | my ($sql, @bind) = @$$v; | ||||
| 1031 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1032 | $sql = $self->_quote($k) . " " . $sql; | ||||
| 1033 | return ($sql, @bind ); | ||||
| 1034 | } | ||||
| 1035 | |||||
| 1036 | # literal SQL without bind | ||||
| 1037 | sub _where_hashpair_SCALAR { | ||||
| 1038 | my ($self, $k, $v) = @_; | ||||
| 1039 | $self->_debug("NOREF($k) means simple key=val: $k $self->{cmp} $v"); | ||||
| 1040 | my $sql = join ' ', $self->_convert($self->_quote($k)), | ||||
| 1041 | $self->_sqlcase($self->{cmp}), | ||||
| 1042 | $self->_convert('?'); | ||||
| 1043 | my @bind = $self->_bindtype($k, $v); | ||||
| 1044 | return ( $sql, @bind); | ||||
| 1045 | } | ||||
| 1046 | |||||
| 1047 | |||||
| 1048 | sub _where_hashpair_UNDEF { | ||||
| 1049 | my ($self, $k, $v) = @_; | ||||
| 1050 | $self->_debug("UNDEF($k) means IS NULL"); | ||||
| 1051 | my $sql = $self->_quote($k) . $self->_sqlcase(' is null'); | ||||
| 1052 | return ($sql); | ||||
| 1053 | } | ||||
| 1054 | |||||
| 1055 | #====================================================================== | ||||
| 1056 | # WHERE: TOP-LEVEL OTHERS (SCALARREF, SCALAR, UNDEF) | ||||
| 1057 | #====================================================================== | ||||
| 1058 | |||||
| 1059 | |||||
| 1060 | sub _where_SCALARREF { | ||||
| 1061 | my ($self, $where) = @_; | ||||
| 1062 | |||||
| 1063 | # literal sql | ||||
| 1064 | $self->_debug("SCALAR(*top) means literal SQL: $$where"); | ||||
| 1065 | return ($$where); | ||||
| 1066 | } | ||||
| 1067 | |||||
| 1068 | |||||
| 1069 | sub _where_SCALAR { | ||||
| 1070 | my ($self, $where) = @_; | ||||
| 1071 | |||||
| 1072 | # literal sql | ||||
| 1073 | $self->_debug("NOREF(*top) means literal SQL: $where"); | ||||
| 1074 | return ($where); | ||||
| 1075 | } | ||||
| 1076 | |||||
| 1077 | |||||
| 1078 | sub _where_UNDEF { | ||||
| 1079 | my ($self) = @_; | ||||
| 1080 | return (); | ||||
| 1081 | } | ||||
| 1082 | |||||
| 1083 | |||||
| 1084 | #====================================================================== | ||||
| 1085 | # WHERE: BUILTIN SPECIAL OPERATORS (-in, -between) | ||||
| 1086 | #====================================================================== | ||||
| 1087 | |||||
| 1088 | |||||
| 1089 | sub _where_field_BETWEEN { | ||||
| 1090 | my ($self, $k, $op, $vals) = @_; | ||||
| 1091 | |||||
| 1092 | my ($label, $and, $placeholder); | ||||
| 1093 | $label = $self->_convert($self->_quote($k)); | ||||
| 1094 | $and = ' ' . $self->_sqlcase('and') . ' '; | ||||
| 1095 | $placeholder = $self->_convert('?'); | ||||
| 1096 | $op = $self->_sqlcase($op); | ||||
| 1097 | |||||
| 1098 | my $invalid_args = "Operator '$op' requires either an arrayref with two defined values or expressions, or a single literal scalarref/arrayref-ref"; | ||||
| 1099 | |||||
| 1100 | my ($clause, @bind) = $self->_SWITCH_refkind($vals, { | ||||
| 1101 | ARRAYREFREF => sub { | ||||
| 1102 | my ($s, @b) = @$$vals; | ||||
| 1103 | $self->_assert_bindval_matches_bindtype(@b); | ||||
| 1104 | ($s, @b); | ||||
| 1105 | }, | ||||
| 1106 | SCALARREF => sub { | ||||
| 1107 | return $$vals; | ||||
| 1108 | }, | ||||
| 1109 | ARRAYREF => sub { | ||||
| 1110 | puke $invalid_args if @$vals != 2; | ||||
| 1111 | |||||
| 1112 | my (@all_sql, @all_bind); | ||||
| 1113 | foreach my $val (@$vals) { | ||||
| 1114 | my ($sql, @bind) = $self->_SWITCH_refkind($val, { | ||||
| 1115 | SCALAR => sub { | ||||
| 1116 | return ($placeholder, $self->_bindtype($k, $val) ); | ||||
| 1117 | }, | ||||
| 1118 | SCALARREF => sub { | ||||
| 1119 | return $$val; | ||||
| 1120 | }, | ||||
| 1121 | ARRAYREFREF => sub { | ||||
| 1122 | my ($sql, @bind) = @$$val; | ||||
| 1123 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1124 | return ($sql, @bind); | ||||
| 1125 | }, | ||||
| 1126 | HASHREF => sub { | ||||
| 1127 | my ($func, $arg, @rest) = %$val; | ||||
| 1128 | puke ("Only simple { -func => arg } functions accepted as sub-arguments to BETWEEN") | ||||
| 1129 | if (@rest or $func !~ /^ \- (.+)/x); | ||||
| 1130 | $self->_where_unary_op ($1 => $arg); | ||||
| 1131 | }, | ||||
| 1132 | FALLBACK => sub { | ||||
| 1133 | puke $invalid_args, | ||||
| 1134 | }, | ||||
| 1135 | }); | ||||
| 1136 | push @all_sql, $sql; | ||||
| 1137 | push @all_bind, @bind; | ||||
| 1138 | } | ||||
| 1139 | |||||
| 1140 | return ( | ||||
| 1141 | (join $and, @all_sql), | ||||
| 1142 | @all_bind | ||||
| 1143 | ); | ||||
| 1144 | }, | ||||
| 1145 | FALLBACK => sub { | ||||
| 1146 | puke $invalid_args, | ||||
| 1147 | }, | ||||
| 1148 | }); | ||||
| 1149 | |||||
| 1150 | my $sql = "( $label $op $clause )"; | ||||
| 1151 | return ($sql, @bind) | ||||
| 1152 | } | ||||
| 1153 | |||||
| 1154 | |||||
| 1155 | sub _where_field_IN { | ||||
| 1156 | my ($self, $k, $op, $vals) = @_; | ||||
| 1157 | |||||
| 1158 | # backwards compatibility : if scalar, force into an arrayref | ||||
| 1159 | $vals = [$vals] if defined $vals && ! ref $vals; | ||||
| 1160 | |||||
| 1161 | my ($label) = $self->_convert($self->_quote($k)); | ||||
| 1162 | my ($placeholder) = $self->_convert('?'); | ||||
| 1163 | $op = $self->_sqlcase($op); | ||||
| 1164 | |||||
| 1165 | my ($sql, @bind) = $self->_SWITCH_refkind($vals, { | ||||
| 1166 | ARRAYREF => sub { # list of choices | ||||
| 1167 | if (@$vals) { # nonempty list | ||||
| 1168 | my (@all_sql, @all_bind); | ||||
| 1169 | |||||
| 1170 | for my $val (@$vals) { | ||||
| 1171 | my ($sql, @bind) = $self->_SWITCH_refkind($val, { | ||||
| 1172 | SCALAR => sub { | ||||
| 1173 | return ($placeholder, $val); | ||||
| 1174 | }, | ||||
| 1175 | SCALARREF => sub { | ||||
| 1176 | return $$val; | ||||
| 1177 | }, | ||||
| 1178 | ARRAYREFREF => sub { | ||||
| 1179 | my ($sql, @bind) = @$$val; | ||||
| 1180 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1181 | return ($sql, @bind); | ||||
| 1182 | }, | ||||
| 1183 | HASHREF => sub { | ||||
| 1184 | my ($func, $arg, @rest) = %$val; | ||||
| 1185 | puke ("Only simple { -func => arg } functions accepted as sub-arguments to IN") | ||||
| 1186 | if (@rest or $func !~ /^ \- (.+)/x); | ||||
| 1187 | $self->_where_unary_op ($1 => $arg); | ||||
| 1188 | }, | ||||
| 1189 | UNDEF => sub { | ||||
| 1190 | puke( | ||||
| 1191 | 'SQL::Abstract before v1.75 used to generate incorrect SQL when the ' | ||||
| 1192 | . "-$op operator was given an undef-containing list: !!!AUDIT YOUR CODE " | ||||
| 1193 | . 'AND DATA!!! (the upcoming Data::Query-based version of SQL::Abstract ' | ||||
| 1194 | . 'will emit the logically correct SQL instead of raising this exception)' | ||||
| 1195 | ); | ||||
| 1196 | }, | ||||
| 1197 | }); | ||||
| 1198 | push @all_sql, $sql; | ||||
| 1199 | push @all_bind, @bind; | ||||
| 1200 | } | ||||
| 1201 | |||||
| 1202 | return ( | ||||
| 1203 | sprintf ('%s %s ( %s )', | ||||
| 1204 | $label, | ||||
| 1205 | $op, | ||||
| 1206 | join (', ', @all_sql) | ||||
| 1207 | ), | ||||
| 1208 | $self->_bindtype($k, @all_bind), | ||||
| 1209 | ); | ||||
| 1210 | } | ||||
| 1211 | else { # empty list : some databases won't understand "IN ()", so DWIM | ||||
| 1212 | my $sql = ($op =~ /\bnot\b/i) ? $self->{sqltrue} : $self->{sqlfalse}; | ||||
| 1213 | return ($sql); | ||||
| 1214 | } | ||||
| 1215 | }, | ||||
| 1216 | |||||
| 1217 | SCALARREF => sub { # literal SQL | ||||
| 1218 | my $sql = $self->_open_outer_paren ($$vals); | ||||
| 1219 | return ("$label $op ( $sql )"); | ||||
| 1220 | }, | ||||
| 1221 | ARRAYREFREF => sub { # literal SQL with bind | ||||
| 1222 | my ($sql, @bind) = @$$vals; | ||||
| 1223 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1224 | $sql = $self->_open_outer_paren ($sql); | ||||
| 1225 | return ("$label $op ( $sql )", @bind); | ||||
| 1226 | }, | ||||
| 1227 | |||||
| 1228 | UNDEF => sub { | ||||
| 1229 | puke "Argument passed to the '$op' operator can not be undefined"; | ||||
| 1230 | }, | ||||
| 1231 | |||||
| 1232 | FALLBACK => sub { | ||||
| 1233 | puke "special op $op requires an arrayref (or scalarref/arrayref-ref)"; | ||||
| 1234 | }, | ||||
| 1235 | }); | ||||
| 1236 | |||||
| 1237 | return ($sql, @bind); | ||||
| 1238 | } | ||||
| 1239 | |||||
| 1240 | # Some databases (SQLite) treat col IN (1, 2) different from | ||||
| 1241 | # col IN ( (1, 2) ). Use this to strip all outer parens while | ||||
| 1242 | # adding them back in the corresponding method | ||||
| 1243 | sub _open_outer_paren { | ||||
| 1244 | my ($self, $sql) = @_; | ||||
| 1245 | |||||
| 1246 | while ( my ($inner) = $sql =~ /^ \s* \( (.*) \) \s* $/xs ) { | ||||
| 1247 | |||||
| 1248 | # there are closing parens inside, need the heavy duty machinery | ||||
| 1249 | # to reevaluate the extraction starting from $sql (full reevaluation) | ||||
| 1250 | if ( $inner =~ /\)/ ) { | ||||
| 1251 | require Text::Balanced; | ||||
| 1252 | |||||
| 1253 | my (undef, $remainder) = do { | ||||
| 1254 | # idiotic design - writes to $@ but *DOES NOT* throw exceptions | ||||
| 1255 | local $@; | ||||
| 1256 | Text::Balanced::extract_bracketed( $sql, '()', qr/\s*/ ); | ||||
| 1257 | }; | ||||
| 1258 | |||||
| 1259 | # the entire expression needs to be a balanced bracketed thing | ||||
| 1260 | # (after an extract no remainder sans trailing space) | ||||
| 1261 | last if defined $remainder and $remainder =~ /\S/; | ||||
| 1262 | } | ||||
| 1263 | |||||
| 1264 | $sql = $inner; | ||||
| 1265 | } | ||||
| 1266 | |||||
| 1267 | $sql; | ||||
| 1268 | } | ||||
| 1269 | |||||
| 1270 | |||||
| 1271 | #====================================================================== | ||||
| 1272 | # ORDER BY | ||||
| 1273 | #====================================================================== | ||||
| 1274 | |||||
| 1275 | sub _order_by { | ||||
| 1276 | my ($self, $arg) = @_; | ||||
| 1277 | |||||
| 1278 | my (@sql, @bind); | ||||
| 1279 | for my $c ($self->_order_by_chunks ($arg) ) { | ||||
| 1280 | $self->_SWITCH_refkind ($c, { | ||||
| 1281 | SCALAR => sub { push @sql, $c }, | ||||
| 1282 | ARRAYREF => sub { push @sql, shift @$c; push @bind, @$c }, | ||||
| 1283 | }); | ||||
| 1284 | } | ||||
| 1285 | |||||
| 1286 | my $sql = @sql | ||||
| 1287 | ? sprintf ('%s %s', | ||||
| 1288 | $self->_sqlcase(' order by'), | ||||
| 1289 | join (', ', @sql) | ||||
| 1290 | ) | ||||
| 1291 | : '' | ||||
| 1292 | ; | ||||
| 1293 | |||||
| 1294 | return wantarray ? ($sql, @bind) : $sql; | ||||
| 1295 | } | ||||
| 1296 | |||||
| 1297 | sub _order_by_chunks { | ||||
| 1298 | my ($self, $arg) = @_; | ||||
| 1299 | |||||
| 1300 | return $self->_SWITCH_refkind($arg, { | ||||
| 1301 | |||||
| 1302 | ARRAYREF => sub { | ||||
| 1303 | map { $self->_order_by_chunks ($_ ) } @$arg; | ||||
| 1304 | }, | ||||
| 1305 | |||||
| 1306 | ARRAYREFREF => sub { | ||||
| 1307 | my ($s, @b) = @$$arg; | ||||
| 1308 | $self->_assert_bindval_matches_bindtype(@b); | ||||
| 1309 | [ $s, @b ]; | ||||
| 1310 | }, | ||||
| 1311 | |||||
| 1312 | SCALAR => sub {$self->_quote($arg)}, | ||||
| 1313 | |||||
| 1314 | UNDEF => sub {return () }, | ||||
| 1315 | |||||
| 1316 | SCALARREF => sub {$$arg}, # literal SQL, no quoting | ||||
| 1317 | |||||
| 1318 | HASHREF => sub { | ||||
| 1319 | # get first pair in hash | ||||
| 1320 | my ($key, $val, @rest) = %$arg; | ||||
| 1321 | |||||
| 1322 | return () unless $key; | ||||
| 1323 | |||||
| 1324 | if ( @rest or not $key =~ /^-(desc|asc)/i ) { | ||||
| 1325 | puke "hash passed to _order_by must have exactly one key (-desc or -asc)"; | ||||
| 1326 | } | ||||
| 1327 | |||||
| 1328 | my $direction = $1; | ||||
| 1329 | |||||
| 1330 | my @ret; | ||||
| 1331 | for my $c ($self->_order_by_chunks ($val)) { | ||||
| 1332 | my ($sql, @bind); | ||||
| 1333 | |||||
| 1334 | $self->_SWITCH_refkind ($c, { | ||||
| 1335 | SCALAR => sub { | ||||
| 1336 | $sql = $c; | ||||
| 1337 | }, | ||||
| 1338 | ARRAYREF => sub { | ||||
| 1339 | ($sql, @bind) = @$c; | ||||
| 1340 | }, | ||||
| 1341 | }); | ||||
| 1342 | |||||
| 1343 | $sql = $sql . ' ' . $self->_sqlcase($direction); | ||||
| 1344 | |||||
| 1345 | push @ret, [ $sql, @bind]; | ||||
| 1346 | } | ||||
| 1347 | |||||
| 1348 | return @ret; | ||||
| 1349 | }, | ||||
| 1350 | }); | ||||
| 1351 | } | ||||
| 1352 | |||||
| 1353 | |||||
| 1354 | #====================================================================== | ||||
| 1355 | # DATASOURCE (FOR NOW, JUST PLAIN TABLE OR LIST OF TABLES) | ||||
| 1356 | #====================================================================== | ||||
| 1357 | |||||
| 1358 | sub _table { | ||||
| 1359 | my $self = shift; | ||||
| 1360 | my $from = shift; | ||||
| 1361 | $self->_SWITCH_refkind($from, { | ||||
| 1362 | ARRAYREF => sub {join ', ', map { $self->_quote($_) } @$from;}, | ||||
| 1363 | SCALAR => sub {$self->_quote($from)}, | ||||
| 1364 | SCALARREF => sub {$$from}, | ||||
| 1365 | }); | ||||
| 1366 | } | ||||
| 1367 | |||||
| 1368 | |||||
| 1369 | #====================================================================== | ||||
| 1370 | # UTILITY FUNCTIONS | ||||
| 1371 | #====================================================================== | ||||
| 1372 | |||||
| 1373 | # highly optimized, as it's called way too often | ||||
| 1374 | sub _quote { | ||||
| 1375 | # my ($self, $label) = @_; | ||||
| 1376 | |||||
| 1377 | 69000 | 14.5ms | return '' unless defined $_[1]; | ||
| 1378 | 69000 | 17.9ms | return ${$_[1]} if ref($_[1]) eq 'SCALAR'; | ||
| 1379 | |||||
| 1380 | 69000 | 25.1ms | unless ($_[0]->{quote_char}) { | ||
| 1381 | 69000 | 89.6ms | 69000 | 634ms | $_[0]->_assert_pass_injection_guard($_[1]); # spent 634ms making 69000 calls to SQL::Abstract::_assert_pass_injection_guard, avg 9µs/call |
| 1382 | 69000 | 43.4ms | return $_[1]; | ||
| 1383 | } | ||||
| 1384 | |||||
| 1385 | my $qref = ref $_[0]->{quote_char}; | ||||
| 1386 | my ($l, $r); | ||||
| 1387 | if (!$qref) { | ||||
| 1388 | ($l, $r) = ( $_[0]->{quote_char}, $_[0]->{quote_char} ); | ||||
| 1389 | } | ||||
| 1390 | elsif ($qref eq 'ARRAY') { | ||||
| 1391 | ($l, $r) = @{$_[0]->{quote_char}}; | ||||
| 1392 | } | ||||
| 1393 | else { | ||||
| 1394 | puke "Unsupported quote_char format: $_[0]->{quote_char}"; | ||||
| 1395 | } | ||||
| 1396 | my $esc = $_[0]->{escape_char} || $r; | ||||
| 1397 | |||||
| 1398 | # parts containing * are naturally unquoted | ||||
| 1399 | return join( $_[0]->{name_sep}||'', map | ||||
| 1400 | { $_ eq '*' ? $_ : do { (my $n = $_) =~ s/(\Q$esc\E|\Q$r\E)/$esc$1/g; $l . $n . $r } } | ||||
| 1401 | ( $_[0]->{name_sep} ? split (/\Q$_[0]->{name_sep}\E/, $_[1] ) : $_[1] ) | ||||
| 1402 | ); | ||||
| 1403 | } | ||||
| 1404 | |||||
| 1405 | |||||
| 1406 | # Conversion, if applicable | ||||
| 1407 | # spent 11.2ms within SQL::Abstract::_convert which was called 6000 times, avg 2µs/call:
# 3000 times (7.45ms+0s) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:668] at line 665, avg 2µs/call
# 3000 times (3.76ms+0s) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:941] at line 937, avg 1µs/call | ||||
| 1408 | #my ($self, $arg) = @_; | ||||
| 1409 | 6000 | 4.44ms | if ($_[0]->{convert}) { | ||
| 1410 | return $_[0]->_sqlcase($_[0]->{convert}) .'(' . $_[1] . ')'; | ||||
| 1411 | } | ||||
| 1412 | 6000 | 12.3ms | return $_[1]; | ||
| 1413 | } | ||||
| 1414 | |||||
| 1415 | # And bindtype | ||||
| 1416 | # spent 16.6ms within SQL::Abstract::_bindtype which was called 3000 times, avg 6µs/call:
# 3000 times (16.6ms+0s) by SQL::Abstract::__ANON__[/usr/share/perl5/SQL/Abstract.pm:668] at line 665, avg 6µs/call | ||||
| 1417 | #my ($self, $col, @vals) = @_; | ||||
| 1418 | # called often - tighten code | ||||
| 1419 | return $_[0]->{bindtype} eq 'columns' | ||||
| 1420 | 3000 | 18.9ms | ? map {[$_[1], $_]} @_[2 .. $#_] | ||
| 1421 | : @_[2 .. $#_] | ||||
| 1422 | ; | ||||
| 1423 | } | ||||
| 1424 | |||||
| 1425 | # Dies if any element of @bind is not in [colname => value] format | ||||
| 1426 | # if bindtype is 'columns'. | ||||
| 1427 | sub _assert_bindval_matches_bindtype { | ||||
| 1428 | # my ($self, @bind) = @_; | ||||
| 1429 | my $self = shift; | ||||
| 1430 | if ($self->{bindtype} eq 'columns') { | ||||
| 1431 | for (@_) { | ||||
| 1432 | if (!defined $_ || ref($_) ne 'ARRAY' || @$_ != 2) { | ||||
| 1433 | puke "bindtype 'columns' selected, you need to pass: [column_name => bind_value]" | ||||
| 1434 | } | ||||
| 1435 | } | ||||
| 1436 | } | ||||
| 1437 | } | ||||
| 1438 | |||||
| 1439 | # spent 11.4ms within SQL::Abstract::_join_sql_clauses which was called 3000 times, avg 4µs/call:
# 3000 times (11.4ms+0s) by SQL::Abstract::_where_HASHREF at line 626, avg 4µs/call | ||||
| 1440 | 3000 | 1.89ms | my ($self, $logic, $clauses_aref, $bind_aref) = @_; | ||
| 1441 | |||||
| 1442 | 3000 | 19.5ms | if (@$clauses_aref > 1) { | ||
| 1443 | my $join = " " . $self->_sqlcase($logic) . " "; | ||||
| 1444 | my $sql = '( ' . join($join, @$clauses_aref) . ' )'; | ||||
| 1445 | return ($sql, @$bind_aref); | ||||
| 1446 | } | ||||
| 1447 | elsif (@$clauses_aref) { | ||||
| 1448 | return ($clauses_aref->[0], @$bind_aref); # no parentheses | ||||
| 1449 | } | ||||
| 1450 | else { | ||||
| 1451 | return (); # if no SQL, ignore @$bind_aref | ||||
| 1452 | } | ||||
| 1453 | } | ||||
| 1454 | |||||
| 1455 | |||||
| 1456 | # Fix SQL case, if so requested | ||||
| 1457 | # spent 20.3ms within SQL::Abstract::_sqlcase which was called 12000 times, avg 2µs/call:
# 6000 times (7.70ms+0s) by DBIx::Class::SQLMaker::select at line 439, avg 1µs/call
# 3000 times (8.40ms+0s) by SQL::Abstract::_where_unary_op at line 674, avg 3µs/call
# 3000 times (4.19ms+0s) by SQL::Abstract::where at line 476, avg 1µs/call | ||||
| 1458 | # LDNOTE: if $self->{case} is true, then it contains 'lower', so we | ||||
| 1459 | # don't touch the argument ... crooked logic, but let's not change it! | ||||
| 1460 | 12000 | 45.6ms | return $_[0]->{case} ? $_[1] : uc($_[1]); | ||
| 1461 | } | ||||
| 1462 | |||||
| 1463 | |||||
| 1464 | #====================================================================== | ||||
| 1465 | # DISPATCHING FROM REFKIND | ||||
| 1466 | #====================================================================== | ||||
| 1467 | |||||
| 1468 | # spent 86.5ms (80.1+6.45) within SQL::Abstract::_refkind which was called 12000 times, avg 7µs/call:
# 12000 times (80.1ms+6.45ms) by SQL::Abstract::_try_refkind at line 1490, avg 7µs/call | ||||
| 1469 | 12000 | 3.01ms | my ($self, $data) = @_; | ||
| 1470 | |||||
| 1471 | 12000 | 2.21ms | return 'UNDEF' unless defined $data; | ||
| 1472 | |||||
| 1473 | # blessed objects are treated like scalars | ||||
| 1474 | 12000 | 57.6ms | 12000 | 6.45ms | my $ref = (Scalar::Util::blessed $data) ? '' : ref $data; # spent 6.45ms making 12000 calls to Scalar::Util::blessed, avg 538ns/call |
| 1475 | |||||
| 1476 | 12000 | 21.5ms | return 'SCALAR' unless $ref; | ||
| 1477 | |||||
| 1478 | 6000 | 1.46ms | my $n_steps = 1; | ||
| 1479 | 6000 | 3.92ms | while ($ref eq 'REF') { | ||
| 1480 | $data = $$data; | ||||
| 1481 | $ref = (Scalar::Util::blessed $data) ? '' : ref $data; | ||||
| 1482 | $n_steps++ if $ref; | ||||
| 1483 | } | ||||
| 1484 | |||||
| 1485 | 6000 | 20.9ms | return ($ref||'SCALAR') . ('REF' x $n_steps); | ||
| 1486 | } | ||||
| 1487 | |||||
| 1488 | sub _try_refkind { | ||||
| 1489 | 12000 | 3.12ms | my ($self, $data) = @_; | ||
| 1490 | 12000 | 17.8ms | 12000 | 86.5ms | my @try = ($self->_refkind($data)); # spent 86.5ms making 12000 calls to SQL::Abstract::_refkind, avg 7µs/call |
| 1491 | 12000 | 5.68ms | push @try, 'SCALAR_or_UNDEF' if $try[0] eq 'SCALAR' || $try[0] eq 'UNDEF'; | ||
| 1492 | 12000 | 2.87ms | push @try, 'FALLBACK'; | ||
| 1493 | 12000 | 30.0ms | return \@try; | ||
| 1494 | } | ||||
| 1495 | |||||
| 1496 | sub _METHOD_FOR_refkind { | ||||
| 1497 | 6000 | 2.11ms | my ($self, $meth_prefix, $data) = @_; | ||
| 1498 | |||||
| 1499 | 6000 | 870µs | my $method; | ||
| 1500 | 6000 | 10.1ms | 6000 | 91.7ms | for (@{$self->_try_refkind($data)}) { # spent 91.7ms making 6000 calls to SQL::Abstract::_try_refkind, avg 15µs/call |
| 1501 | 6000 | 34.5ms | 6000 | 9.05ms | $method = $self->can($meth_prefix."_".$_) # spent 9.05ms making 6000 calls to UNIVERSAL::can, avg 2µs/call |
| 1502 | and last; | ||||
| 1503 | } | ||||
| 1504 | |||||
| 1505 | 6000 | 16.7ms | return $method || puke "cannot dispatch on '$meth_prefix' for ".$self->_refkind($data); | ||
| 1506 | } | ||||
| 1507 | |||||
| 1508 | |||||
| 1509 | sub _SWITCH_refkind { | ||||
| 1510 | 6000 | 3.55ms | my ($self, $data, $dispatch_table) = @_; | ||
| 1511 | |||||
| 1512 | 6000 | 967µs | my $coderef; | ||
| 1513 | 6000 | 8.70ms | 6000 | 62.2ms | for (@{$self->_try_refkind($data)}) { # spent 62.2ms making 6000 calls to SQL::Abstract::_try_refkind, avg 10µs/call |
| 1514 | 12000 | 8.30ms | $coderef = $dispatch_table->{$_} | ||
| 1515 | and last; | ||||
| 1516 | } | ||||
| 1517 | |||||
| 1518 | 6000 | 1.11ms | puke "no dispatch entry for ".$self->_refkind($data) | ||
| 1519 | unless $coderef; | ||||
| 1520 | |||||
| 1521 | 6000 | 23.6ms | 6000 | 565ms | $coderef->(); # spent 520ms making 3000 calls to SQL::Abstract::__ANON__[SQL/Abstract.pm:941], avg 173µs/call
# spent 45.3ms making 3000 calls to SQL::Abstract::__ANON__[SQL/Abstract.pm:668], avg 15µs/call |
| 1522 | } | ||||
| 1523 | |||||
| - - | |||||
| 1527 | #====================================================================== | ||||
| 1528 | # VALUES, GENERATE, AUTOLOAD | ||||
| 1529 | #====================================================================== | ||||
| 1530 | |||||
| 1531 | # LDNOTE: original code from nwiger, didn't touch code in that section | ||||
| 1532 | # I feel the AUTOLOAD stuff should not be the default, it should | ||||
| 1533 | # only be activated on explicit demand by user. | ||||
| 1534 | |||||
| 1535 | sub values { | ||||
| 1536 | my $self = shift; | ||||
| 1537 | my $data = shift || return; | ||||
| 1538 | puke "Argument to ", __PACKAGE__, "->values must be a \\%hash" | ||||
| 1539 | unless ref $data eq 'HASH'; | ||||
| 1540 | |||||
| 1541 | my @all_bind; | ||||
| 1542 | foreach my $k ( sort keys %$data ) { | ||||
| 1543 | my $v = $data->{$k}; | ||||
| 1544 | $self->_SWITCH_refkind($v, { | ||||
| 1545 | ARRAYREF => sub { | ||||
| 1546 | if ($self->{array_datatypes}) { # array datatype | ||||
| 1547 | push @all_bind, $self->_bindtype($k, $v); | ||||
| 1548 | } | ||||
| 1549 | else { # literal SQL with bind | ||||
| 1550 | my ($sql, @bind) = @$v; | ||||
| 1551 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1552 | push @all_bind, @bind; | ||||
| 1553 | } | ||||
| 1554 | }, | ||||
| 1555 | ARRAYREFREF => sub { # literal SQL with bind | ||||
| 1556 | my ($sql, @bind) = @${$v}; | ||||
| 1557 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1558 | push @all_bind, @bind; | ||||
| 1559 | }, | ||||
| 1560 | SCALARREF => sub { # literal SQL without bind | ||||
| 1561 | }, | ||||
| 1562 | SCALAR_or_UNDEF => sub { | ||||
| 1563 | push @all_bind, $self->_bindtype($k, $v); | ||||
| 1564 | }, | ||||
| 1565 | }); | ||||
| 1566 | } | ||||
| 1567 | |||||
| 1568 | return @all_bind; | ||||
| 1569 | } | ||||
| 1570 | |||||
| 1571 | sub generate { | ||||
| 1572 | my $self = shift; | ||||
| 1573 | |||||
| 1574 | my(@sql, @sqlq, @sqlv); | ||||
| 1575 | |||||
| 1576 | for (@_) { | ||||
| 1577 | my $ref = ref $_; | ||||
| 1578 | if ($ref eq 'HASH') { | ||||
| 1579 | for my $k (sort keys %$_) { | ||||
| 1580 | my $v = $_->{$k}; | ||||
| 1581 | my $r = ref $v; | ||||
| 1582 | my $label = $self->_quote($k); | ||||
| 1583 | if ($r eq 'ARRAY') { | ||||
| 1584 | # literal SQL with bind | ||||
| 1585 | my ($sql, @bind) = @$v; | ||||
| 1586 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1587 | push @sqlq, "$label = $sql"; | ||||
| 1588 | push @sqlv, @bind; | ||||
| 1589 | } elsif ($r eq 'SCALAR') { | ||||
| 1590 | # literal SQL without bind | ||||
| 1591 | push @sqlq, "$label = $$v"; | ||||
| 1592 | } else { | ||||
| 1593 | push @sqlq, "$label = ?"; | ||||
| 1594 | push @sqlv, $self->_bindtype($k, $v); | ||||
| 1595 | } | ||||
| 1596 | } | ||||
| 1597 | push @sql, $self->_sqlcase('set'), join ', ', @sqlq; | ||||
| 1598 | } elsif ($ref eq 'ARRAY') { | ||||
| 1599 | # unlike insert(), assume these are ONLY the column names, i.e. for SQL | ||||
| 1600 | for my $v (@$_) { | ||||
| 1601 | my $r = ref $v; | ||||
| 1602 | if ($r eq 'ARRAY') { # literal SQL with bind | ||||
| 1603 | my ($sql, @bind) = @$v; | ||||
| 1604 | $self->_assert_bindval_matches_bindtype(@bind); | ||||
| 1605 | push @sqlq, $sql; | ||||
| 1606 | push @sqlv, @bind; | ||||
| 1607 | } elsif ($r eq 'SCALAR') { # literal SQL without bind | ||||
| 1608 | # embedded literal SQL | ||||
| 1609 | push @sqlq, $$v; | ||||
| 1610 | } else { | ||||
| 1611 | push @sqlq, '?'; | ||||
| 1612 | push @sqlv, $v; | ||||
| 1613 | } | ||||
| 1614 | } | ||||
| 1615 | push @sql, '(' . join(', ', @sqlq) . ')'; | ||||
| 1616 | } elsif ($ref eq 'SCALAR') { | ||||
| 1617 | # literal SQL | ||||
| 1618 | push @sql, $$_; | ||||
| 1619 | } else { | ||||
| 1620 | # strings get case twiddled | ||||
| 1621 | push @sql, $self->_sqlcase($_); | ||||
| 1622 | } | ||||
| 1623 | } | ||||
| 1624 | |||||
| 1625 | my $sql = join ' ', @sql; | ||||
| 1626 | |||||
| 1627 | # this is pretty tricky | ||||
| 1628 | # if ask for an array, return ($stmt, @bind) | ||||
| 1629 | # otherwise, s/?/shift @sqlv/ to put it inline | ||||
| 1630 | if (wantarray) { | ||||
| 1631 | return ($sql, @sqlv); | ||||
| 1632 | } else { | ||||
| 1633 | 1 while $sql =~ s/\?/my $d = shift(@sqlv); | ||||
| 1634 | ref $d ? $d->[1] : $d/e; | ||||
| 1635 | return $sql; | ||||
| 1636 | } | ||||
| 1637 | } | ||||
| 1638 | |||||
| 1639 | |||||
| 1640 | sub DESTROY { 1 } | ||||
| 1641 | |||||
| 1642 | sub AUTOLOAD { | ||||
| 1643 | # This allows us to check for a local, then _form, attr | ||||
| 1644 | my $self = shift; | ||||
| 1645 | my($name) = $AUTOLOAD =~ /.*::(.+)/; | ||||
| 1646 | return $self->generate($name, @_); | ||||
| 1647 | } | ||||
| 1648 | |||||
| 1649 | 1 | 11µs | 1; | ||
| 1650 | |||||
| - - | |||||
| 1653 | __END__ | ||||
# spent 164ms within SQL::Abstract::CORE:match which was called 114001 times, avg 1µs/call:
# 75000 times (157ms+0s) by SQL::Abstract::_assert_pass_injection_guard at line 198, avg 2µs/call
# 18000 times (3.13ms+0s) by List::Util::first at line 637, avg 174ns/call
# 18000 times (2.69ms+0s) by List::Util::first or SQL::Abstract::_where_hashpair_HASHREF at line 887, avg 150ns/call
# 3000 times (1.79ms+0s) by SQL::Abstract::_where_HASHREF at line 585, avg 595ns/call
# once (4µs+0s) by DBIx::Class::Row::BEGIN@12 at line 33 | |||||
# spent 16µs within SQL::Abstract::CORE:qr which was called 16 times, avg 1µs/call:
# 6 times (4µs+0s) by DBIx::Class::Row::BEGIN@12 at line 48, avg 683ns/call
# 5 times (7µs+0s) by DBIx::Class::Row::BEGIN@12 at line 39, avg 1µs/call
# once (2µs+0s) by SQL::Abstract::new at line 160
# once (1µs+0s) by SQL::Abstract::new at line 161
# once (1µs+0s) by SQL::Abstract::new at line 164
# once (1µs+0s) by SQL::Abstract::new at line 163
# once (800ns+0s) by SQL::Abstract::new at line 187 | |||||
# spent 101ms within SQL::Abstract::CORE:regcomp which was called 108001 times, avg 935ns/call:
# 75000 times (69.0ms+0s) by SQL::Abstract::_assert_pass_injection_guard at line 198, avg 920ns/call
# 18000 times (16.6ms+0s) by List::Util::first at line 637, avg 922ns/call
# 15000 times (15.4ms+0s) by List::Util::first at line 887, avg 1µs/call
# once (16µs+0s) by SQL::Abstract::new at line 160 | |||||
sub SQL::Abstract::CORE:sort; # opcode | |||||
# spent 11.6ms within SQL::Abstract::CORE:subst which was called 15000 times, avg 773ns/call:
# 3000 times (4.12ms+0s) by SQL::Abstract::_where_hashpair_HASHREF at line 868, avg 1µs/call
# 3000 times (3.03ms+0s) by SQL::Abstract::_where_hashpair_HASHREF at line 869, avg 1µs/call
# 3000 times (3.01ms+0s) by SQL::Abstract::_where_hashpair_HASHREF at line 867, avg 1µs/call
# 3000 times (759µs+0s) by SQL::Abstract::_where_hashpair_HASHREF at line 874, avg 253ns/call
# 3000 times (674µs+0s) by SQL::Abstract::_where_hashpair_HASHREF at line 877, avg 225ns/call |