Filename | /var/www/foswikidev/core/lib/Foswiki/Query/Node.pm |
Statements | Executed 286714 statements in 681ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
26438 | 9 | 8 | 543ms | 1.21s | evaluate (recurses: max depth 2, inclusive time 761ms) | Foswiki::Query::Node::
8760 | 1 | 1 | 132ms | 299ms | _getField | Foswiki::Query::Node::
240 | 3 | 3 | 8.90ms | 12.5ms | simplify (recurses: max depth 1, inclusive time 9.91ms) | Foswiki::Query::Node::
320 | 3 | 2 | 2.59ms | 2.90ms | toString (recurses: max depth 4, inclusive time 5.99ms) | Foswiki::Query::Node::
147 | 2 | 1 | 1.76ms | 3.30ms | newLeaf | Foswiki::Query::Node::
400 | 3 | 3 | 1.05ms | 1.75ms | evaluatesToConstant (recurses: max depth 1, inclusive time 280µs) | Foswiki::Query::Node::
80 | 1 | 1 | 756µs | 1.34ms | _freeze | Foswiki::Query::Node::
1 | 1 | 1 | 445µs | 983µs | BEGIN@75 | Foswiki::Query::Node::
40 | 1 | 1 | 139µs | 139µs | isEmpty | Foswiki::Query::Node::
1 | 1 | 1 | 14µs | 26µs | BEGIN@29 | Foswiki::Query::Node::
1 | 1 | 1 | 10µs | 21µs | BEGIN@93 | Foswiki::Query::Node::
1 | 1 | 1 | 9µs | 14µs | BEGIN@30 | Foswiki::Query::Node::
1 | 1 | 1 | 9µs | 24µs | BEGIN@109 | Foswiki::Query::Node::
1 | 1 | 1 | 9µs | 35µs | BEGIN@34 | Foswiki::Query::Node::
1 | 1 | 1 | 9µs | 102µs | BEGIN@35 | Foswiki::Query::Node::
1 | 1 | 1 | 8µs | 36µs | BEGIN@41 | Foswiki::Query::Node::
1 | 1 | 1 | 8µs | 32µs | BEGIN@42 | Foswiki::Query::Node::
1 | 1 | 1 | 5µs | 5µs | BEGIN@31 | Foswiki::Query::Node::
1 | 1 | 1 | 4µs | 4µs | BEGIN@37 | Foswiki::Query::Node::
0 | 0 | 0 | 0s | 0s | _makeArray | Foswiki::Query::Node::
0 | 0 | 0 | 0s | 0s | emptyExpression | Foswiki::Query::Node::
0 | 0 | 0 | 0s | 0s | tokens | Foswiki::Query::Node::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Query::Node | ||||
6 | |||||
7 | A Node object is a single node in a query (either a tree node or a leaf node). | ||||
8 | A tree of node objects represents a query over the Foswiki database. | ||||
9 | |||||
10 | Fields are given by name, and values by strings or numbers. | ||||
11 | |||||
12 | A query object implements the =evaluate= method as its general | ||||
13 | contract with the rest of the world. This method is a "reference implementation" - | ||||
14 | it does a brute force evaluation of the expression represented by the node in a given | ||||
15 | data domain. It is expected that smarter store implementations will analyse the parse tree | ||||
16 | and derive as many optimisations as possible, minimising fallback to this brute force | ||||
17 | evaluation. | ||||
18 | |||||
19 | The reference implementation of evaluation uses the =getField= method in the | ||||
20 | {Store}{QueryAlgorithm} to get data from the store. This further decouples the query | ||||
21 | object from the detail of the store implementation. | ||||
22 | |||||
23 | See Foswiki::Store::QueryAlgorithms for a full spec of the interface to | ||||
24 | query algorithms. | ||||
25 | |||||
26 | =cut | ||||
27 | |||||
28 | package Foswiki::Query::Node; | ||||
29 | 2 | 27µs | 2 | 39µs | # spent 26µs (14+13) within Foswiki::Query::Node::BEGIN@29 which was called:
# once (14µs+13µs) by Foswiki::Infix::Node::BEGIN@131 at line 29 # spent 26µs making 1 call to Foswiki::Query::Node::BEGIN@29
# spent 13µs making 1 call to strict::import |
30 | 2 | 27µs | 2 | 18µs | # spent 14µs (9+4) within Foswiki::Query::Node::BEGIN@30 which was called:
# once (9µs+4µs) by Foswiki::Infix::Node::BEGIN@131 at line 30 # spent 14µs making 1 call to Foswiki::Query::Node::BEGIN@30
# spent 4µs making 1 call to warnings::import |
31 | 2 | 33µs | 1 | 5µs | # spent 5µs within Foswiki::Query::Node::BEGIN@31 which was called:
# once (5µs+0s) by Foswiki::Infix::Node::BEGIN@131 at line 31 # spent 5µs making 1 call to Foswiki::Query::Node::BEGIN@31 |
32 | 1 | 8µs | our @ISA = ('Foswiki::Infix::Node'); | ||
33 | |||||
34 | 2 | 28µs | 2 | 61µs | # spent 35µs (9+26) within Foswiki::Query::Node::BEGIN@34 which was called:
# once (9µs+26µs) by Foswiki::Infix::Node::BEGIN@131 at line 34 # spent 35µs making 1 call to Foswiki::Query::Node::BEGIN@34
# spent 26µs making 1 call to Exporter::import |
35 | 2 | 29µs | 2 | 194µs | # spent 102µs (9+93) within Foswiki::Query::Node::BEGIN@35 which was called:
# once (9µs+93µs) by Foswiki::Infix::Node::BEGIN@131 at line 35 # spent 102µs making 1 call to Foswiki::Query::Node::BEGIN@35
# spent 93µs making 1 call to Error::import |
36 | |||||
37 | 2 | 24µs | 1 | 4µs | # spent 4µs within Foswiki::Query::Node::BEGIN@37 which was called:
# once (4µs+0s) by Foswiki::Infix::Node::BEGIN@131 at line 37 # spent 4µs making 1 call to Foswiki::Query::Node::BEGIN@37 |
38 | |||||
39 | # <DEBUG SUPPORT> | ||||
40 | |||||
41 | 2 | 30µs | 2 | 63µs | # spent 36µs (8+28) within Foswiki::Query::Node::BEGIN@41 which was called:
# once (8µs+28µs) by Foswiki::Infix::Node::BEGIN@131 at line 41 # spent 36µs making 1 call to Foswiki::Query::Node::BEGIN@41
# spent 28µs making 1 call to constant::import |
42 | 2 | 112µs | 2 | 57µs | # spent 32µs (8+24) within Foswiki::Query::Node::BEGIN@42 which was called:
# once (8µs+24µs) by Foswiki::Infix::Node::BEGIN@131 at line 42 # spent 32µs making 1 call to Foswiki::Query::Node::BEGIN@42
# spent 24µs making 1 call to constant::import |
43 | |||||
44 | # Cache of the names of $Foswiki::cfg items that are accessible | ||||
45 | 1 | 100ns | our $isAccessibleCfg; | ||
46 | |||||
47 | =begin TML | ||||
48 | |||||
49 | ---++ PUBLIC %aliases | ||||
50 | A hash mapping short aliases for META: entry names. For example, this hash | ||||
51 | maps 'form' to 'META:FORM'. Published because extensions (search | ||||
52 | implementations) have made use of it in the past, though not part of the | ||||
53 | offical API. | ||||
54 | |||||
55 | This hash is maintained by Foswiki::Meta and is *strictly read-only* | ||||
56 | |||||
57 | ---++ PUBLIC %isArrayType | ||||
58 | Maps META: entry type names to true if the type is an array type (such as | ||||
59 | FIELD, ATTACHMENT or PREFERENCE). Published because extensions (search | ||||
60 | implementations) have made use of it in the past, though not part of the | ||||
61 | offical API. The type name should be given without the leading 'META:' | ||||
62 | |||||
63 | This hash is maintained by Foswiki::Meta and is *strictly read-only* | ||||
64 | |||||
65 | =cut | ||||
66 | |||||
67 | # These used to be declared here, but have been refactored back into | ||||
68 | # Foswiki::Meta | ||||
69 | 1 | 1µs | *aliases = \%Foswiki::Meta::aliases; | ||
70 | 1 | 300ns | *isArrayType = \%Foswiki::Meta::isArrayType; | ||
71 | |||||
72 | 1 | 100ns | our $emptyExprOp; | ||
73 | 1 | 100ns | our $commaOp; | ||
74 | |||||
75 | # spent 983µs (445+538) within Foswiki::Query::Node::BEGIN@75 which was called:
# once (445µs+538µs) by Foswiki::Infix::Node::BEGIN@131 at line 80 | ||||
76 | 1 | 62µs | require Foswiki::Query::OP_empty; | ||
77 | 1 | 3µs | 1 | 30µs | $emptyExprOp = Foswiki::Query::OP_empty->new(); # spent 30µs making 1 call to Foswiki::Query::OP_empty::new |
78 | 1 | 68µs | require Foswiki::Query::OP_comma; | ||
79 | 1 | 6µs | 1 | 29µs | $commaOp = Foswiki::Query::OP_comma->new(); # spent 29µs making 1 call to Foswiki::Query::OP_comma::new |
80 | 1 | 63µs | 1 | 983µs | } # spent 983µs making 1 call to Foswiki::Query::Node::BEGIN@75 |
81 | |||||
82 | sub emptyExpression { | ||||
83 | my $this = shift; | ||||
84 | return $this->newNode($emptyExprOp); | ||||
85 | } | ||||
86 | |||||
87 | # spent 2.90ms (2.59+308µs) within Foswiki::Query::Node::toString which was called 320 times, avg 9µs/call:
# 160 times (960µs+-960µs) by Foswiki::Query::Node::toString at line 102, avg 0s/call
# 120 times (1.11ms+-1.11ms) by Foswiki::Query::Node::toString at line 94, avg 0s/call
# 40 times (521µs+2.37ms) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 158 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 72µs/call | ||||
88 | 320 | 85µs | my $a = shift; | ||
89 | 320 | 49µs | return 'undef' unless defined($a); | ||
90 | |||||
91 | # Suppress the recursion check; the tree can easily be more than | ||||
92 | # 100 levels deep. | ||||
93 | 2 | 118µs | 2 | 32µs | # spent 21µs (10+11) within Foswiki::Query::Node::BEGIN@93 which was called:
# once (10µs+11µs) by Foswiki::Infix::Node::BEGIN@131 at line 93 # spent 21µs making 1 call to Foswiki::Query::Node::BEGIN@93
# spent 11µs making 1 call to warnings::unimport |
94 | 320 | 1.42ms | 440 | 243µs | if ( UNIVERSAL::isa( $a, 'Foswiki::Query::Node' ) ) { # spent 243µs making 320 calls to UNIVERSAL::isa, avg 761ns/call
# spent 3.57ms making 120 calls to Foswiki::Query::Node::toString, avg 30µs/call, recursion: max depth 3, sum of overlapping time 3.57ms |
95 | return | ||||
96 | '{ op => ' | ||||
97 | . $a->{op} | ||||
98 | . ', params => ' | ||||
99 | . toString( $a->{params} ) . ' }'; | ||||
100 | } | ||||
101 | 200 | 106µs | if ( ref($a) eq 'ARRAY' ) { | ||
102 | 280 | 712µs | 160 | 0s | return '[' . join( ',', map { toString($_) } @$a ) . ']'; # spent 2.42ms making 160 calls to Foswiki::Query::Node::toString, avg 15µs/call, recursion: max depth 4, sum of overlapping time 2.42ms |
103 | } | ||||
104 | 80 | 28µs | if ( ref($a) eq 'HASH' ) { | ||
105 | return | ||||
106 | '{' | ||||
107 | . join( ',', map { "$_=>" . toString( $a->{$_} ) } keys %$a ) . '}'; | ||||
108 | } | ||||
109 | 2 | 1.27ms | 2 | 40µs | # spent 24µs (9+15) within Foswiki::Query::Node::BEGIN@109 which was called:
# once (9µs+15µs) by Foswiki::Infix::Node::BEGIN@131 at line 109 # spent 24µs making 1 call to Foswiki::Query::Node::BEGIN@109
# spent 15µs making 1 call to warnings::import |
110 | 80 | 204µs | 80 | 64µs | if ( UNIVERSAL::isa( $a, 'Foswiki::Meta' ) ) { # spent 64µs making 80 calls to UNIVERSAL::isa, avg 805ns/call |
111 | return $a->stringify(); | ||||
112 | } | ||||
113 | 80 | 227µs | return $a; | ||
114 | } | ||||
115 | |||||
116 | 1 | 300ns | my $ind = 0; | ||
117 | |||||
118 | # </DEBUG SUPPORT> | ||||
119 | |||||
120 | # STATIC overrides Foswiki::Infix::Node | ||||
121 | # We expand config vars to constant strings during the parse, because | ||||
122 | # otherwise we'd have to export the knowledge of config vars out to other | ||||
123 | # engines that may evaluate queries instead of the default evaluator. | ||||
124 | # spent 3.30ms (1.76+1.54) within Foswiki::Query::Node::newLeaf which was called 147 times, avg 22µs/call:
# 87 times (1.30ms+1.10ms) by Foswiki::Infix::Parser::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm:306] at line 276 of /var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm, avg 28µs/call
# 60 times (457µs+434µs) by Foswiki::Infix::Parser::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm:306] at line 260 of /var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm, avg 15µs/call | ||||
125 | 147 | 139µs | my ( $class, $val, $type ) = @_; | ||
126 | |||||
127 | 147 | 207µs | if ( $type == Foswiki::Infix::Node::NAME | ||
128 | && $val =~ m/^({[A-Z][A-Z0-9_]*})+$/i ) | ||||
129 | { | ||||
130 | |||||
131 | # config var name, make sure it's accessible. | ||||
132 | 3 | 6µs | unless ( defined $isAccessibleCfg ) { | ||
133 | $isAccessibleCfg = | ||||
134 | 1 | 56µs | { map { $_ => 1 } @{ $Foswiki::cfg{AccessibleCFG} } }; | ||
135 | } | ||||
136 | $val = | ||||
137 | 3 | 89µs | ( $isAccessibleCfg->{$val} ) ? eval( '$Foswiki::cfg' . $val ) : ''; # spent 4µs executing statements in string eval
# spent 3µs executing statements in string eval
# spent 3µs executing statements in string eval | ||
138 | 3 | 27µs | 3 | 31µs | return $class->SUPER::newLeaf( $val, Foswiki::Infix::Node::STRING ); # spent 31µs making 3 calls to Foswiki::Infix::Node::newLeaf, avg 10µs/call |
139 | } | ||||
140 | else { | ||||
141 | 144 | 1.32ms | 144 | 1.51ms | return $class->SUPER::newLeaf( $val, $type ); # spent 1.51ms making 144 calls to Foswiki::Infix::Node::newLeaf, avg 10µs/call |
142 | } | ||||
143 | } | ||||
144 | |||||
145 | =begin TML | ||||
146 | |||||
147 | ---++ ObjectMethod evaluate(...) -> $result | ||||
148 | |||||
149 | Evaluate this node by invoking the =evaluate= method of the attached operator. | ||||
150 | The return result is either an array ref (for many results) or a scalar (for a | ||||
151 | single result) | ||||
152 | |||||
153 | This is the reference evaluator for queries. However it may not be the only | ||||
154 | engine that evaluates them; external engines, such as SQL, might be delegated | ||||
155 | the responsibility of evaluating queries in a search context. | ||||
156 | |||||
157 | Name resolution depends on the context in which the name is used. A name | ||||
158 | on the LHS of the dot and where operators may only be a form name, or a META: | ||||
159 | name, referred to as a "restricted name". A name anywhere else can be | ||||
160 | a META: name, a field name, or one of the shortcuts (such as "web", "name" | ||||
161 | etc). Fields and forms are looked up by calling the =getField= and | ||||
162 | =getForm= methods in the query engine respectively. | ||||
163 | |||||
164 | =cut | ||||
165 | |||||
166 | # spent 1.21s (543ms+664ms) within Foswiki::Query::Node::evaluate which was called 26438 times, avg 46µs/call:
# 8769 times (406ms+-406ms) by Foswiki::Query::ConditionalOP::evalTest at line 69 of /var/www/foswikidev/core/lib/Foswiki/Query/ConditionalOP.pm, avg 0s/call
# 8769 times (36.9ms+-36.9ms) by Foswiki::Query::ConditionalOP::evalTest at line 70 of /var/www/foswikidev/core/lib/Foswiki/Query/ConditionalOP.pm, avg 0s/call
# 8760 times (98.7ms+1.10s) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 214 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 137µs/call
# 80 times (546µs+0s) by Foswiki::Query::Node::simplify at line 348, avg 7µs/call
# 43 times (392µs+6.93ms) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm:50] at line 42 of /var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm, avg 170µs/call
# 12 times (82µs+-82µs) by Foswiki::Query::OP_and::evaluate at line 35 of /var/www/foswikidev/core/lib/Foswiki/Query/OP_and.pm, avg 0s/call
# 3 times (22µs+-22µs) by Foswiki::Query::OP_or::evaluate at line 35 of /var/www/foswikidev/core/lib/Foswiki/Query/OP_or.pm, avg 0s/call
# once (8µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/QUERY.pm:68] at line 66 of /var/www/foswikidev/core/lib/Foswiki/Macros/QUERY.pm
# once (4µs+-4µs) by Foswiki::If::OP_allows::evaluate at line 45 of /var/www/foswikidev/core/lib/Foswiki/If/OP_allows.pm | ||||
167 | 26438 | 5.31ms | my $this = shift; | ||
168 | ASSERT( scalar(@_) % 2 == 0 ) if DEBUG; | ||||
169 | 26438 | 2.88ms | my $result; | ||
170 | |||||
171 | print STDERR ( ' ' x $ind ) . $this->stringify() if MONITOR_EVAL; | ||||
172 | |||||
173 | 26438 | 18.8ms | if ( !ref( $this->{op} ) ) { | ||
174 | 17614 | 15.4ms | my %domain = @_; | ||
175 | 17614 | 17.8ms | if ( $this->{op} == Foswiki::Infix::Node::NAME | ||
176 | && defined $domain{data} ) | ||||
177 | { | ||||
178 | print STDERR ' NAME' if MONITOR_EVAL; | ||||
179 | 8760 | 12.2ms | if ( lc( $this->{params}[0] ) eq 'now' ) { | ||
180 | $result = time(); | ||||
181 | } | ||||
182 | elsif ( lc( $this->{params}[0] ) eq 'undefined' ) { | ||||
183 | $result = undef; | ||||
184 | } | ||||
185 | else { | ||||
186 | |||||
187 | # a name; either the form name or a field name. | ||||
188 | # Look it up in $domain{data} | ||||
189 | 8760 | 241ms | eval "require $Foswiki::cfg{Store}{QueryAlgorithm}"; # spent 22.7ms executing statements in 8760 string evals (merged) | ||
190 | 8760 | 1.83ms | if ($@) { | ||
191 | print STDERR ' BOOM ' if MONITOR_EVAL; | ||||
192 | die $@; | ||||
193 | } | ||||
194 | 8760 | 7.12ms | my $name = $this->{params}[0]; | ||
195 | 8760 | 6.26ms | my $realname = $Foswiki::Meta::aliases{$name} || $name; | ||
196 | 8760 | 6.42ms | if ( $domain{restricted_name} && $realname !~ /^META:/ ) { | ||
197 | |||||
198 | # Only a form name and META: expressions are | ||||
199 | # legal on the LHS of a dot or where expression | ||||
200 | print STDERR " form $name" if MONITOR_EVAL; | ||||
201 | $result = | ||||
202 | $Foswiki::cfg{Store}{QueryAlgorithm} | ||||
203 | ->getForm( $this, $domain{data}, $name ); | ||||
204 | } | ||||
205 | else { | ||||
206 | print STDERR " field $name" if MONITOR_EVAL; | ||||
207 | 8760 | 61.8ms | 8760 | 14.3ms | $name = $realname # spent 14.3ms making 8760 calls to UNIVERSAL::isa, avg 2µs/call |
208 | if UNIVERSAL::isa( $domain{data}, 'Foswiki::Meta' ); | ||||
209 | 8760 | 17.0ms | 8760 | 299ms | $result = $this->_getField( $domain{data}, $name ); # spent 299ms making 8760 calls to Foswiki::Query::Node::_getField, avg 34µs/call |
210 | } | ||||
211 | } | ||||
212 | } | ||||
213 | else { | ||||
214 | print STDERR ' constant' if MONITOR_EVAL; | ||||
215 | 8854 | 5.16ms | $result = $this->{params}[0]; | ||
216 | } | ||||
217 | } | ||||
218 | else { | ||||
219 | print STDERR " {\n" if MONITOR_EVAL; | ||||
220 | 8824 | 621µs | $ind++ if MONITOR_EVAL; | ||
221 | 8824 | 13.0ms | my %params = @_; | ||
222 | 8824 | 3.59ms | delete $params{no_fields}; # kill semaphore | ||
223 | 8824 | 37.4ms | 8824 | 1.11s | $result = $this->{op}->evaluate( $this, %params ); # spent 1.10s making 8763 calls to Foswiki::Query::OP_eq::evaluate, avg 126µs/call
# spent 4.37ms making 16 calls to Foswiki::If::OP_dollar::evaluate, avg 273µs/call
# spent 1.85ms making 8 calls to Foswiki::Query::OP_and::evaluate, avg 231µs/call
# spent 1.26ms making 6 calls to Foswiki::Query::OP_ne::evaluate, avg 210µs/call
# spent 1.18ms making 13 calls to Foswiki::If::OP_defined::evaluate, avg 91µs/call
# spent 515µs making 1 call to Foswiki::Query::OP_or::evaluate
# spent 433µs making 4 calls to Foswiki::If::OP_istopic::evaluate, avg 108µs/call
# spent 208µs making 12 calls to Foswiki::If::OP_context::evaluate, avg 17µs/call
# spent 180µs making 1 call to Foswiki::If::OP_allows::evaluate |
224 | $ind-- if MONITOR_EVAL; | ||||
225 | 8824 | 6.07ms | print STDERR ( ' ' x $ind ) . '}' . $this->{op}->{name} | ||
226 | if MONITOR_EVAL; | ||||
227 | } | ||||
228 | if (MONITOR_EVAL) { | ||||
229 | print STDERR ' -> ' . toString($result); | ||||
230 | my %domain = @_; | ||||
231 | print STDERR " IN " . $domain{tom}->getPath() . "\n" | ||||
232 | if ref( $domain{tom} ) && !$ind; | ||||
233 | print STDERR "\n"; | ||||
234 | } | ||||
235 | 26438 | 87.1ms | return $result; | ||
236 | } | ||||
237 | |||||
238 | # Private method to fetch field values | ||||
239 | # spent 299ms (132+167) within Foswiki::Query::Node::_getField which was called 8760 times, avg 34µs/call:
# 8760 times (132ms+167ms) by Foswiki::Query::Node::evaluate at line 209, avg 34µs/call | ||||
240 | 8760 | 6.23ms | my ( $this, $data, $name ) = @_; | ||
241 | |||||
242 | 8760 | 90.2ms | 17520 | 167ms | if ( UNIVERSAL::isa( $data, 'Foswiki::Meta' ) ) { # spent 162ms making 8760 calls to Foswiki::Store::Interfaces::QueryAlgorithm::getField, avg 18µs/call
# spent 4.79ms making 8760 calls to UNIVERSAL::isa, avg 547ns/call |
243 | |||||
244 | # If the data is a Foswiki::Meta, pass on to the query algorithm | ||||
245 | return $Foswiki::cfg{Store}{QueryAlgorithm} | ||||
246 | ->getField( $this, $data, $name ); | ||||
247 | } | ||||
248 | |||||
249 | if ( ref($data) eq 'ARRAY' ) { | ||||
250 | |||||
251 | # Array objects are returned during evaluation, e.g. when | ||||
252 | # a subset of an array is matched for further processing. | ||||
253 | |||||
254 | # Indexing an array object. The index will be one of: | ||||
255 | # 1. An integer, which is an implicit index='x' query | ||||
256 | # 2. A name, which is an implicit name='x' query | ||||
257 | if ( $name =~ m/^\d+$/ ) { | ||||
258 | |||||
259 | # Integer index | ||||
260 | return $data->[$name]; | ||||
261 | } | ||||
262 | |||||
263 | # String index | ||||
264 | my @res; | ||||
265 | |||||
266 | # Get all array entries that match the field | ||||
267 | foreach my $f (@$data) { | ||||
268 | my $val = $this->_getField( $f, $name ); | ||||
269 | push( @res, $val ) if defined($val); | ||||
270 | } | ||||
271 | return \@res if ( scalar(@res) ); | ||||
272 | |||||
273 | # The field name wasn't explicitly seen in any of the records. | ||||
274 | # Try again, this time matching 'name' and returning 'value' | ||||
275 | foreach my $f (@$data) { | ||||
276 | next unless ref($f) eq 'HASH'; | ||||
277 | if ( $f->{name} | ||||
278 | && $f->{name} eq $name | ||||
279 | && defined $f->{value} ) | ||||
280 | { | ||||
281 | push( @res, $f->{value} ); | ||||
282 | } | ||||
283 | } | ||||
284 | return \@res if ( scalar(@res) ); | ||||
285 | return undef; | ||||
286 | } | ||||
287 | |||||
288 | if ( ref($data) eq 'HASH' ) { | ||||
289 | |||||
290 | # A hash object may be returned when a sub-object of a Foswiki::Meta | ||||
291 | # object has been matched. | ||||
292 | return $data->{ $this->{params}[0] }; | ||||
293 | } | ||||
294 | |||||
295 | # Last ditch - treat it as a constant | ||||
296 | return $this->{params}[0]; | ||||
297 | } | ||||
298 | |||||
299 | =begin TML | ||||
300 | |||||
301 | ---++ ObjectMethod evaluatesToConstant(%opts) | ||||
302 | |||||
303 | Support for expression optimisation/hoisting. | ||||
304 | |||||
305 | Determine if this node evaluates to a constant or not. "Constant" is defined | ||||
306 | as "anything that doesn't involve actually looking in searched topics". | ||||
307 | This function takes the same parameters (%domain) as evaluate(). Note that | ||||
308 | no reference to the tom or data web or topic will be made, so you can | ||||
309 | simply pass an arbitrary Foswiki::Meta. | ||||
310 | |||||
311 | =cut | ||||
312 | |||||
313 | # spent 1.75ms (1.05+701µs) within Foswiki::Query::Node::evaluatesToConstant which was called 400 times, avg 4µs/call:
# 240 times (633µs+773µs) by Foswiki::Query::Node::simplify at line 347, avg 6µs/call
# 120 times (280µs+-280µs) by Foswiki::Query::OP::evaluatesToConstant at line 66 of /var/www/foswikidev/core/lib/Foswiki/Query/OP.pm, avg 0s/call
# 40 times (141µs+208µs) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 81 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 9µs/call | ||||
314 | 400 | 71µs | my $this = shift; | ||
315 | 400 | 63µs | my $c = 0; | ||
316 | 400 | 511µs | 120 | 981µs | if ( ref( $this->{op} ) ) { # spent 981µs making 120 calls to Foswiki::Query::OP::evaluatesToConstant, avg 8µs/call |
317 | $c = $this->{op}->evaluatesToConstant( $this, @_ ); | ||||
318 | } | ||||
319 | elsif ( $this->{op} == Foswiki::Infix::Node::NUMBER ) { | ||||
320 | $c = 1; | ||||
321 | } | ||||
322 | elsif ( $this->{op} == Foswiki::Infix::Node::STRING ) { | ||||
323 | $c = 1; | ||||
324 | } | ||||
325 | print STDERR $this->stringify() . " is " | ||||
326 | . ( $c ? '' : 'not ' ) | ||||
327 | . "constant\n" | ||||
328 | if MONITOR_FOLD; | ||||
329 | 400 | 7.28ms | return $c; | ||
330 | } | ||||
331 | |||||
332 | =begin TML | ||||
333 | |||||
334 | ---++ ObjectMethod simplify(%opts) | ||||
335 | |||||
336 | Simplify the query by spotting constant expressions and evaluating them, | ||||
337 | replacing the constant expression with an atomic value in the expression tree. | ||||
338 | This function takes the same parameters (%domain) as evaluate(). Note that | ||||
339 | no reference to the tom or data web or topic will be made, so you can | ||||
340 | simply pass an arbitrary Foswiki::Meta. | ||||
341 | |||||
342 | =cut | ||||
343 | |||||
344 | # spent 12.5ms (8.90+3.58) within Foswiki::Query::Node::simplify which was called 240 times, avg 52µs/call:
# 160 times (7.70ms+-7.70ms) by Foswiki::Query::Node::simplify at line 354, avg 0s/call
# 40 times (471µs+8.32ms) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 79 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 220µs/call
# 40 times (728µs+2.96ms) by Foswiki::Store::Interfaces::QueryAlgorithm::query at line 98 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 92µs/call | ||||
345 | 240 | 57µs | my $this = shift; | ||
346 | |||||
347 | 240 | 725µs | 240 | 1.41ms | if ( $this->evaluatesToConstant(@_) ) { # spent 1.41ms making 240 calls to Foswiki::Query::Node::evaluatesToConstant, avg 6µs/call |
348 | 80 | 172µs | 80 | 546µs | my $c = $this->evaluate(@_); # spent 546µs making 80 calls to Foswiki::Query::Node::evaluate, avg 7µs/call |
349 | 80 | 23µs | $c = 0 unless defined $c; | ||
350 | 80 | 179µs | 80 | 1.34ms | $this->_freeze($c); # spent 1.34ms making 80 calls to Foswiki::Query::Node::_freeze, avg 17µs/call |
351 | } | ||||
352 | else { | ||||
353 | 160 | 149µs | for my $f ( @{ $this->{params} } ) { | ||
354 | 240 | 1.08ms | 400 | 290µs | if ( UNIVERSAL::can( $f, 'simplify' ) ) { # spent 290µs making 240 calls to UNIVERSAL::can, avg 1µs/call
# spent 9.91ms making 160 calls to Foswiki::Query::Node::simplify, avg 62µs/call, recursion: max depth 1, sum of overlapping time 9.91ms |
355 | $f->simplify(@_); | ||||
356 | } | ||||
357 | } | ||||
358 | } | ||||
359 | } | ||||
360 | |||||
361 | # spent 1.34ms (756µs+583µs) within Foswiki::Query::Node::_freeze which was called 80 times, avg 17µs/call:
# 80 times (756µs+583µs) by Foswiki::Query::Node::simplify at line 350, avg 17µs/call | ||||
362 | 80 | 75µs | my ( $this, $c ) = @_; | ||
363 | |||||
364 | 80 | 320µs | 80 | 301µs | if ( ref($c) eq 'ARRAY' ) { # spent 301µs making 80 calls to Foswiki::Query::OP::isNumber, avg 4µs/call |
365 | $this->_makeArray($c); | ||||
366 | } | ||||
367 | elsif ( ref($c) eq 'HASH' ) { | ||||
368 | $this->convertToLeaf( Foswiki::Infix::Node::HASH, $c ); | ||||
369 | } | ||||
370 | elsif ( ref($c) eq 'Foswiki::Meta' ) { | ||||
371 | $this->convertToLeaf( Foswiki::Infix::Node::META, $c ); | ||||
372 | } | ||||
373 | elsif ( Foswiki::Query::OP::isNumber($c) ) { | ||||
374 | $this->convertToLeaf( Foswiki::Infix::Node::NUMBER, $c ); | ||||
375 | } | ||||
376 | else { | ||||
377 | |||||
378 | #Item10703: can't convert a non-scalar to a STRING without further processing. | ||||
379 | 80 | 208µs | 80 | 282µs | if ( ref($c) eq '' ) { # spent 282µs making 80 calls to Foswiki::Infix::Node::convertToLeaf, avg 4µs/call |
380 | $this->convertToLeaf( Foswiki::Infix::Node::STRING, $c ); | ||||
381 | } | ||||
382 | else { | ||||
383 | print STDERR "_freeze" . ref($c) . "\n" if MONITOR_FOLD; | ||||
384 | } | ||||
385 | } | ||||
386 | } | ||||
387 | |||||
388 | sub _makeArray { | ||||
389 | my ( $this, $array ) = @_; | ||||
390 | if ( scalar(@$array) == 0 ) { | ||||
391 | $this->{op} = $emptyExprOp; | ||||
392 | } | ||||
393 | elsif ( scalar(@$array) == 1 ) { | ||||
394 | die unless defined $array->[0]; | ||||
395 | $this->_freeze( $array->[0] ); | ||||
396 | } | ||||
397 | else { | ||||
398 | $this->{op} = $commaOp; | ||||
399 | $this->{params}[0] = Foswiki::Query::Node->newNode($commaOp); | ||||
400 | $this->{params}[0]->_freeze( shift(@$array) ); | ||||
401 | $this->{params}[1] = Foswiki::Query::Node->newNode($commaOp); | ||||
402 | $this->{params}[1]->_freeze($array); | ||||
403 | } | ||||
404 | } | ||||
405 | |||||
406 | =begin TML | ||||
407 | |||||
408 | ---++ ObjectMethod tokens() -> [] | ||||
409 | Provided for compatibility with Foswiki::Search::Node | ||||
410 | |||||
411 | =cut | ||||
412 | |||||
413 | sub tokens { | ||||
414 | return []; | ||||
415 | } | ||||
416 | |||||
417 | =begin TML | ||||
418 | |||||
419 | ---++ ObjectMethod isEmpty() -> $boolean | ||||
420 | Provided for compatibility with Foswiki::Search::Node | ||||
421 | |||||
422 | =cut | ||||
423 | |||||
424 | # spent 139µs within Foswiki::Query::Node::isEmpty which was called 40 times, avg 3µs/call:
# 40 times (139µs+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::query at line 84 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 3µs/call | ||||
425 | 40 | 133µs | return 0; | ||
426 | } | ||||
427 | |||||
428 | 1 | 5µs | 1; | ||
429 | __END__ |