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 | _assert_pass_injection_guard | SQL::Abstract::
3000 | 1 | 1 | 225ms | 975ms | _where_hashpair_HASHREF | SQL::Abstract::
114001 | 5 | 1 | 164ms | 164ms | CORE:match (opcode) | SQL::Abstract::
3000 | 1 | 1 | 131ms | 392ms | _where_unary_op | SQL::Abstract::
108001 | 4 | 1 | 101ms | 101ms | CORE:regcomp (opcode) | SQL::Abstract::
3000 | 1 | 1 | 86.8ms | 1.13s | _where_HASHREF | SQL::Abstract::
12000 | 1 | 1 | 80.1ms | 86.5ms | _refkind | SQL::Abstract::
6000 | 2 | 1 | 77.5ms | 605ms | _SWITCH_refkind (recurses: max depth 1, inclusive time 99.8ms) | SQL::Abstract::
12000 | 2 | 1 | 67.4ms | 154ms | _try_refkind | SQL::Abstract::
6000 | 2 | 1 | 52.5ms | 153ms | _METHOD_FOR_refkind | SQL::Abstract::
3000 | 1 | 1 | 51.5ms | 520ms | __ANON__[:941] | SQL::Abstract::
3000 | 1 | 1 | 44.1ms | 1.45s | where | SQL::Abstract::
3000 | 1 | 1 | 35.1ms | 1.26s | _recurse_where | SQL::Abstract::
3000 | 1 | 1 | 21.3ms | 45.3ms | __ANON__[:668] | SQL::Abstract::
12000 | 3 | 1 | 20.3ms | 20.3ms | _sqlcase | SQL::Abstract::
3000 | 1 | 1 | 16.6ms | 16.6ms | _bindtype | SQL::Abstract::
15000 | 5 | 1 | 11.6ms | 11.6ms | CORE:subst (opcode) | SQL::Abstract::
3000 | 1 | 1 | 11.4ms | 11.4ms | _join_sql_clauses | SQL::Abstract::
6000 | 2 | 1 | 11.2ms | 11.2ms | _convert | SQL::Abstract::
3000 | 1 | 1 | 8.69ms | 8.69ms | _debug | SQL::Abstract::
6000 | 2 | 1 | 2.93ms | 2.93ms | CORE:sort (opcode) | SQL::Abstract::
1 | 1 | 1 | 42µs | 64µs | new | SQL::Abstract::
16 | 7 | 1 | 16µs | 16µs | CORE:qr (opcode) | SQL::Abstract::
1 | 1 | 1 | 16µs | 23µs | BEGIN@3 | SQL::Abstract::
1 | 1 | 1 | 15µs | 23µs | BEGIN@86 | SQL::Abstract::
1 | 1 | 1 | 8µs | 15µs | BEGIN@9 | SQL::Abstract::
1 | 1 | 1 | 8µs | 8µs | BEGIN@12 | SQL::Abstract::
1 | 1 | 1 | 7µs | 12µs | BEGIN@4 | SQL::Abstract::
1 | 1 | 1 | 4µs | 4µs | BEGIN@5 | SQL::Abstract::
1 | 1 | 1 | 4µs | 4µs | BEGIN@6 | SQL::Abstract::
1 | 1 | 1 | 3µs | 3µs | BEGIN@7 | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | AUTOLOAD | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | DESTROY | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1105] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1108] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1117] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1120] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1125] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1131] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1134] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1144] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1147] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1174] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1177] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1182] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1188] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1196] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1215] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1220] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1226] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1230] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1234] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1281] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1282] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1304] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1310] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1312] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1314] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1316] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1337] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1340] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1349] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1362] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1363] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1364] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1554] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1559] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1561] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:1564] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:236] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:237] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:238] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:309] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:316] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:324] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:328] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:333] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:378] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:384] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:387] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:399] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:403] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:532] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:538] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:540] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:542] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:547] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:549] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:601] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:635] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:637] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:671] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:688] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:694] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:702] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:710] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:714] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:718] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:731] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:735] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:739] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:751] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:755] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:759] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:891] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:911] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:920] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:932] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:959] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | __ANON__[:962] | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _assert_bindval_matches_bindtype | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _insert_ARRAYREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _insert_ARRAYREFREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _insert_HASHREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _insert_SCALARREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _insert_returning | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _insert_values | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _open_outer_paren | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _order_by | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _order_by_chunks | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _quote | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _table | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_ARRAYREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_ARRAYREFREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_SCALAR | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_SCALARREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_UNDEF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_field_BETWEEN | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_field_IN | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_field_IS | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_field_op_ARRAYREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_hashpair_ARRAYREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_hashpair_ARRAYREFREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_hashpair_SCALAR | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_hashpair_SCALARREF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_hashpair_UNDEF | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_op_ANDOR | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_op_BOOL | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_op_IDENT | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_op_NEST | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | _where_op_VALUE | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | delete | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | generate | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | insert | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | is_literal_value | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | is_plain_value | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | select | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | update | SQL::Abstract::
0 | 0 | 0 | 0s | 0s | values | SQL::Abstract::
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 |