Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Store/VC/Store.pm |
Statements | Executed 3297 statements in 22.1ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
70 | 1 | 1 | 8.26ms | 93.8s | readTopic | Foswiki::Store::VC::Store::
272 | 3 | 3 | 7.21ms | 35.6ms | topicExists | Foswiki::Store::VC::Store::
1 | 1 | 1 | 2.20ms | 1.66s | query | Foswiki::Store::VC::Store::
344 | 3 | 1 | 1.09ms | 1.09ms | CORE:subst (opcode) | Foswiki::Store::VC::Store::
1 | 1 | 1 | 753µs | 170ms | eachTopic | Foswiki::Store::VC::Store::
2 | 1 | 1 | 109µs | 621ms | getRevisionHistory | Foswiki::Store::VC::Store::
2 | 1 | 1 | 80µs | 348µs | webExists | Foswiki::Store::VC::Store::
1 | 1 | 1 | 55µs | 62µs | finish | Foswiki::Store::VC::Store::
1 | 1 | 1 | 24µs | 31µs | BEGIN@33 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 23µs | 62µs | BEGIN@39 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 20µs | 442µs | BEGIN@40 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 18µs | 54µs | BEGIN@574 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 16µs | 34µs | BEGIN@34 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 16µs | 45µs | BEGIN@576 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 9µs | 9µs | BEGIN@42 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 9µs | 9µs | BEGIN@43 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 9µs | 9µs | BEGIN@36 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 9µs | 9µs | BEGIN@44 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 9µs | 9µs | BEGIN@46 | Foswiki::Store::VC::Store::
1 | 1 | 1 | 2µs | 2µs | CORE:match (opcode) | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | atomicLock | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | atomicLockInfo | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | atomicUnlock | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | attachmentExists | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | copyAttachment | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | delRev | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | eachAttachment | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | eachChange | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | eachWeb | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getApproxRevTime | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getAttachmentVersionInfo | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getHandler | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getLease | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getNextRevision | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getRevisionAtTime | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getRevisionDiff | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | getVersionInfo | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | moveAttachment | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | moveTopic | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | moveWeb | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | openAttachment | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | remove | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | removeSpuriousLeases | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | repRev | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | saveAttachment | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | saveTopic | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | setLease | Foswiki::Store::VC::Store::
0 | 0 | 0 | 0s | 0s | testAttachment | Foswiki::Store::VC::Store::
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::Store::VC::Store | ||||
6 | |||||
7 | Almost-complete implementation of =Foswiki::Store=. The methods | ||||
8 | of this class implement the =Foswiki::Store= interface. | ||||
9 | |||||
10 | The store uses a "handler" class to handle all interactions with the | ||||
11 | actual version control system (and via it with the actual file system). | ||||
12 | A "handler" is created for each individual file in the file system, and | ||||
13 | this handler then brokers all requests to open, read, write etc the file. | ||||
14 | The handler object must implement the interface specified by | ||||
15 | =Foswiki::Store::VC::Handler=. | ||||
16 | |||||
17 | The main additional responsibilities of _this_ class are to support storing | ||||
18 | Foswiki meta-data in plain text files, and to ensure that the =Foswiki::Meta= | ||||
19 | for a page is maintained in synchronisation with the files on disk. | ||||
20 | |||||
21 | All that is required to create a working store is to subclass this class | ||||
22 | and override the 'new' method to specify the actual handler to use. See | ||||
23 | Foswiki::Store::RcsWrap for an example subclass. | ||||
24 | |||||
25 | For readers who are familiar with Foswiki version 1.0, the functionality | ||||
26 | in this class _previously_ resided in =Foswiki::Store=. | ||||
27 | |||||
28 | These methods are documented in the Foswiki:Store abstract base class | ||||
29 | |||||
30 | =cut | ||||
31 | |||||
32 | package Foswiki::Store::VC::Store; | ||||
33 | 2 | 44µs | 2 | 38µs | # spent 31µs (24+7) within Foswiki::Store::VC::Store::BEGIN@33 which was called:
# once (24µs+7µs) by Foswiki::Store::RcsWrap::BEGIN@23 at line 33 # spent 31µs making 1 call to Foswiki::Store::VC::Store::BEGIN@33
# spent 7µs making 1 call to strict::import |
34 | 2 | 48µs | 2 | 51µs | # spent 34µs (16+17) within Foswiki::Store::VC::Store::BEGIN@34 which was called:
# once (16µs+17µs) by Foswiki::Store::RcsWrap::BEGIN@23 at line 34 # spent 34µs making 1 call to Foswiki::Store::VC::Store::BEGIN@34
# spent 17µs making 1 call to warnings::import |
35 | |||||
36 | 2 | 69µs | 1 | 9µs | # spent 9µs within Foswiki::Store::VC::Store::BEGIN@36 which was called:
# once (9µs+0s) by Foswiki::Store::RcsWrap::BEGIN@23 at line 36 # spent 9µs making 1 call to Foswiki::Store::VC::Store::BEGIN@36 |
37 | 1 | 11µs | our @ISA = ('Foswiki::Store'); | ||
38 | |||||
39 | 2 | 47µs | 2 | 100µs | # spent 62µs (23+38) within Foswiki::Store::VC::Store::BEGIN@39 which was called:
# once (23µs+38µs) by Foswiki::Store::RcsWrap::BEGIN@23 at line 39 # spent 62µs making 1 call to Foswiki::Store::VC::Store::BEGIN@39
# spent 38µs making 1 call to Assert::import |
40 | 2 | 51µs | 2 | 864µs | # spent 442µs (20+422) within Foswiki::Store::VC::Store::BEGIN@40 which was called:
# once (20µs+422µs) by Foswiki::Store::RcsWrap::BEGIN@23 at line 40 # spent 442µs making 1 call to Foswiki::Store::VC::Store::BEGIN@40
# spent 422µs making 1 call to Error::import |
41 | |||||
42 | 2 | 44µs | 1 | 9µs | # spent 9µs within Foswiki::Store::VC::Store::BEGIN@42 which was called:
# once (9µs+0s) by Foswiki::Store::RcsWrap::BEGIN@23 at line 42 # spent 9µs making 1 call to Foswiki::Store::VC::Store::BEGIN@42 |
43 | 2 | 38µs | 1 | 9µs | # spent 9µs within Foswiki::Store::VC::Store::BEGIN@43 which was called:
# once (9µs+0s) by Foswiki::Store::RcsWrap::BEGIN@23 at line 43 # spent 9µs making 1 call to Foswiki::Store::VC::Store::BEGIN@43 |
44 | 2 | 82µs | 1 | 9µs | # spent 9µs within Foswiki::Store::VC::Store::BEGIN@44 which was called:
# once (9µs+0s) by Foswiki::Store::RcsWrap::BEGIN@23 at line 44 # spent 9µs making 1 call to Foswiki::Store::VC::Store::BEGIN@44 |
45 | |||||
46 | # spent 9µs within Foswiki::Store::VC::Store::BEGIN@46 which was called:
# once (9µs+0s) by Foswiki::Store::RcsWrap::BEGIN@23 at line 53 | ||||
47 | |||||
48 | # Do a dynamic 'use locale' for this module | ||||
49 | 1 | 9µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
50 | require locale; | ||||
51 | import locale(); | ||||
52 | } | ||||
53 | 1 | 4.09ms | 1 | 9µs | } # spent 9µs making 1 call to Foswiki::Store::VC::Store::BEGIN@46 |
54 | |||||
55 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
56 | # whether they are references or not. That way this method is "golden | ||||
57 | # documentation" of the live fields in the object. | ||||
58 | # spent 62µs (55+7) within Foswiki::Store::VC::Store::finish which was called:
# once (55µs+7µs) by Foswiki::finish at line 2100 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm | ||||
59 | 3 | 52µs | my $this = shift; | ||
60 | 1 | 7µs | $this->SUPER::finish(); # spent 7µs making 1 call to Foswiki::Store::finish | ||
61 | undef $this->{searchFn}; | ||||
62 | } | ||||
63 | |||||
64 | # SMELL: this module does not respect $Foswiki::inUnitTestMode; tests | ||||
65 | # just sit on top of the store which is configured in the current $Foswiki::cfg. | ||||
66 | # Most of the time this is ok, as store listeners will be told that | ||||
67 | # the store is in test mode, so caches should be unaffected. However | ||||
68 | # it's very untidy, potentially risky, and causes grief when unit tests | ||||
69 | # don't clean up after themselves. | ||||
70 | |||||
71 | # PACKAGE PRIVATE | ||||
72 | # Get a handler for the given object in the store. | ||||
73 | sub getHandler { | ||||
74 | |||||
75 | #my ( $this, $web, $topic, $attachment ) = @_; | ||||
76 | ASSERT( 0, "Must be implemented by subclasses" ) if DEBUG; | ||||
77 | } | ||||
78 | |||||
79 | # spent 93.8s (8.26ms+93.8) within Foswiki::Store::VC::Store::readTopic which was called 70 times, avg 1.34s/call:
# 70 times (8.26ms+93.8s) by Foswiki::Meta::loadVersion at line 1051 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm, avg 1.34s/call | ||||
80 | 1602 | 8.46ms | my ( $this, $topicObject, $version ) = @_; | ||
81 | |||||
82 | 70 | 1.18ms | my ( $gotRev, $isLatest ) = $this->askListeners( $topicObject, $version ); # spent 1.18ms making 70 calls to Foswiki::Store::askListeners, avg 17µs/call | ||
83 | |||||
84 | if ( defined($gotRev) and ( $gotRev > 0 or ($isLatest) ) ) { | ||||
85 | return ( $gotRev, $isLatest ); | ||||
86 | } | ||||
87 | 70 | 257µs | ASSERT( not $isLatest ) if DEBUG; # spent 257µs making 70 calls to Assert::ASSERTS_OFF, avg 4µs/call | ||
88 | |||||
89 | 70 | 6.01ms | my $handler = $this->getHandler($topicObject); # spent 6.01ms making 70 calls to Foswiki::Store::RcsWrap::getHandler, avg 86µs/call | ||
90 | $isLatest = 0; | ||||
91 | |||||
92 | # check that the requested revision actually exists | ||||
93 | 1 | 2µs | if ( defined $version && $version =~ /^\d+$/ ) { # spent 2µs making 1 call to Foswiki::Store::VC::Store::CORE:match | ||
94 | if ( $version == 0 || !$handler->revisionExists($version) ) { | ||||
95 | $version = $handler->getLatestRevisionID(); | ||||
96 | } | ||||
97 | } | ||||
98 | else { | ||||
99 | undef $version; # if it's a non-numeric string, we need to return undef | ||||
100 | # "...$version is defined but refers to a version that does not exist, then $rev is undef" | ||||
101 | } | ||||
102 | |||||
103 | 70 | 12.1ms | ( my $text, $isLatest ) = $handler->getRevision($version); # spent 12.1ms making 70 calls to Foswiki::Store::VC::RcsWrapHandler::getRevision, avg 173µs/call | ||
104 | unless ( defined $text ) { | ||||
105 | ASSERT( not $isLatest ) if DEBUG; | ||||
106 | return ( undef, $isLatest ); | ||||
107 | } | ||||
108 | |||||
109 | 70 | 536µs | $text =~ s/\r//g; # Remove carriage returns # spent 536µs making 70 calls to Foswiki::Store::VC::Store::CORE:subst, avg 8µs/call | ||
110 | 70 | 131ms | $topicObject->setEmbeddedStoreForm($text); # spent 131ms making 70 calls to Foswiki::Meta::setEmbeddedStoreForm, avg 1.87ms/call | ||
111 | |||||
112 | 70 | 3.90ms | unless ( $handler->noCheckinPending() ) { # spent 3.90ms making 70 calls to Foswiki::Store::VC::Handler::noCheckinPending, avg 56µs/call | ||
113 | |||||
114 | # If a checkin is pending, fix the TOPICINFO | ||||
115 | 21 | 428µs | my $ri = $topicObject->get('TOPICINFO'); # spent 428µs making 21 calls to Foswiki::Meta::get, avg 20µs/call | ||
116 | 21 | 92.8s | my $truth = $handler->getInfo($version); # spent 92.8s making 21 calls to Foswiki::Store::VC::RcsWrapHandler::getInfo, avg 4.42s/call | ||
117 | for my $i (qw(author version date)) { | ||||
118 | $ri->{$i} = $truth->{$i}; | ||||
119 | } | ||||
120 | } | ||||
121 | |||||
122 | $gotRev = $version; | ||||
123 | unless ( defined $gotRev ) { | ||||
124 | |||||
125 | # First try the just-loaded for the revision. | ||||
126 | 70 | 1.87ms | my $ri = $topicObject->get('TOPICINFO'); # spent 1.87ms making 70 calls to Foswiki::Meta::get, avg 27µs/call | ||
127 | $gotRev = $ri->{version} if defined $ri; | ||||
128 | } | ||||
129 | if ( !defined $gotRev ) { | ||||
130 | |||||
131 | # No revision from any other source; must be latest | ||||
132 | 3 | 790ms | $gotRev = $handler->getLatestRevisionID(); # spent 790ms making 3 calls to Foswiki::Store::VC::Handler::getLatestRevisionID, avg 263ms/call | ||
133 | 3 | 26µs | ASSERT( defined $gotRev ) if DEBUG; # spent 26µs making 3 calls to Assert::ASSERTS_OFF, avg 9µs/call | ||
134 | } | ||||
135 | |||||
136 | # Add attachments that are new from reading the pub directory. | ||||
137 | # Only check the currently requested topic. | ||||
138 | if ( $Foswiki::cfg{RCS}{AutoAttachPubFiles} | ||||
139 | && $topicObject->isSessionTopic() ) | ||||
140 | { | ||||
141 | my @knownAttachments = $topicObject->find('FILEATTACHMENT'); | ||||
142 | my @attachmentsFoundInPub = | ||||
143 | $handler->synchroniseAttachmentsList( \@knownAttachments ); | ||||
144 | my @validAttachmentsFound; | ||||
145 | foreach my $foundAttachment (@attachmentsFoundInPub) { | ||||
146 | |||||
147 | # test if the attachment filename is valid without having to | ||||
148 | # be sanitized. If not, ignore it. | ||||
149 | my $validated = Foswiki::Sandbox::validateAttachmentName( | ||||
150 | $foundAttachment->{name} ); | ||||
151 | unless ( defined $validated | ||||
152 | && $validated eq $foundAttachment->{name} ) | ||||
153 | { | ||||
154 | |||||
155 | print STDERR 'AutoAttachPubFiles ignoring ' | ||||
156 | . $foundAttachment->{name} . ' in ' | ||||
157 | . $topicObject->getPath() | ||||
158 | . ' - not a valid Foswiki Attachment filename'; | ||||
159 | } | ||||
160 | else { | ||||
161 | push @validAttachmentsFound, $foundAttachment; | ||||
162 | $this->tellListeners( | ||||
163 | verb => 'autoattach', | ||||
164 | newmeta => $topicObject, | ||||
165 | newattachment => $foundAttachment | ||||
166 | ); | ||||
167 | } | ||||
168 | } | ||||
169 | |||||
170 | $topicObject->putAll( 'FILEATTACHMENT', @validAttachmentsFound ) | ||||
171 | if @validAttachmentsFound; | ||||
172 | } | ||||
173 | |||||
174 | 70 | 264µs | ASSERT( defined($gotRev) ) if DEBUG; # spent 264µs making 70 calls to Assert::ASSERTS_OFF, avg 4µs/call | ||
175 | return ( $gotRev, $isLatest ); | ||||
176 | } | ||||
177 | |||||
178 | sub moveAttachment { | ||||
179 | my ( $this, $oldTopicObject, $oldAttachment, $newTopicObject, | ||||
180 | $newAttachment, $cUID ) | ||||
181 | = @_; | ||||
182 | |||||
183 | my $handler = $this->getHandler( $oldTopicObject, $oldAttachment ); | ||||
184 | if ( $handler->storedDataExists() ) { | ||||
185 | $handler->moveAttachment( $this, $newTopicObject->web, | ||||
186 | $newTopicObject->topic, $newAttachment ); | ||||
187 | $this->tellListeners( | ||||
188 | verb => 'update', | ||||
189 | oldmeta => $oldTopicObject, | ||||
190 | oldattachment => $oldAttachment, | ||||
191 | newmeta => $newTopicObject, | ||||
192 | newattachment => $newAttachment | ||||
193 | ); | ||||
194 | $handler->recordChange( $cUID, 0 ); | ||||
195 | } | ||||
196 | } | ||||
197 | |||||
198 | sub copyAttachment { | ||||
199 | my ( $this, $oldTopicObject, $oldAttachment, $newTopicObject, | ||||
200 | $newAttachment, $cUID ) | ||||
201 | = @_; | ||||
202 | |||||
203 | my $handler = $this->getHandler( $oldTopicObject, $oldAttachment ); | ||||
204 | if ( $handler->storedDataExists() ) { | ||||
205 | $handler->copyAttachment( $this, $newTopicObject->web, | ||||
206 | $newTopicObject->topic, $newAttachment ); | ||||
207 | $this->tellListeners( | ||||
208 | verb => 'insert', | ||||
209 | newmeta => $newTopicObject, | ||||
210 | newattachment => $newAttachment | ||||
211 | ); | ||||
212 | $handler->recordChange( $cUID, 0 ); | ||||
213 | } | ||||
214 | } | ||||
215 | |||||
216 | sub attachmentExists { | ||||
217 | my ( $this, $topicObject, $att ) = @_; | ||||
218 | my $handler = $this->getHandler( $topicObject, $att ); | ||||
219 | return $handler->storedDataExists(); | ||||
220 | } | ||||
221 | |||||
222 | sub moveTopic { | ||||
223 | my ( $this, $oldTopicObject, $newTopicObject, $cUID ) = @_; | ||||
224 | ASSERT($cUID) if DEBUG; | ||||
225 | |||||
226 | my $handler = $this->getHandler( $oldTopicObject, '' ); | ||||
227 | my $rev = $handler->getLatestRevisionID(); | ||||
228 | |||||
229 | $handler->moveTopic( $this, $newTopicObject->web, $newTopicObject->topic ); | ||||
230 | |||||
231 | $this->tellListeners( | ||||
232 | verb => 'update', | ||||
233 | oldmeta => $oldTopicObject, | ||||
234 | newmeta => $newTopicObject | ||||
235 | ); | ||||
236 | |||||
237 | if ( $newTopicObject->web ne $oldTopicObject->web ) { | ||||
238 | |||||
239 | # Record that it was moved away | ||||
240 | $handler->recordChange( $cUID, $rev ); | ||||
241 | } | ||||
242 | |||||
243 | $handler = $this->getHandler( $newTopicObject, '' ); | ||||
244 | $handler->recordChange( $cUID, $rev ); | ||||
245 | } | ||||
246 | |||||
247 | sub moveWeb { | ||||
248 | my ( $this, $oldWebObject, $newWebObject, $cUID ) = @_; | ||||
249 | ASSERT($cUID) if DEBUG; | ||||
250 | |||||
251 | my $handler = $this->getHandler($oldWebObject); | ||||
252 | $handler->moveWeb( $newWebObject->web ); | ||||
253 | |||||
254 | $this->tellListeners( | ||||
255 | verb => 'update', | ||||
256 | oldmeta => $oldWebObject, | ||||
257 | newmeta => $newWebObject | ||||
258 | ); | ||||
259 | |||||
260 | # We have to log in the new web, otherwise we would re-create the dir with | ||||
261 | # a useless .changes. See Item9278 | ||||
262 | $handler = $this->getHandler($newWebObject); | ||||
263 | $handler->recordChange( $cUID, 0, 'Moved from ' . $oldWebObject->web ); | ||||
264 | } | ||||
265 | |||||
266 | sub testAttachment { | ||||
267 | my ( $this, $topicObject, $attachment, $test ) = @_; | ||||
268 | my $handler = $this->getHandler( $topicObject, $attachment ); | ||||
269 | return $handler->test($test); | ||||
270 | } | ||||
271 | |||||
272 | sub openAttachment { | ||||
273 | my ( $this, $topicObject, $att, $mode, @opts ) = @_; | ||||
274 | |||||
275 | my $handler = $this->getHandler( $topicObject, $att ); | ||||
276 | return $handler->openStream( $mode, @opts ); | ||||
277 | } | ||||
278 | |||||
279 | # spent 621ms (109µs+621) within Foswiki::Store::VC::Store::getRevisionHistory which was called 2 times, avg 310ms/call:
# 2 times (109µs+621ms) by Foswiki::Meta::getRevisionHistory at line 2337 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm, avg 310ms/call | ||||
280 | 10 | 97µs | my ( $this, $topicObject, $attachment ) = @_; | ||
281 | |||||
282 | 2 | 34µs | my $itr = $this->askListenersRevisionHistory( $topicObject, $attachment ); # spent 34µs making 2 calls to Foswiki::Store::askListenersRevisionHistory, avg 17µs/call | ||
283 | |||||
284 | if ( defined($itr) ) { | ||||
285 | return $itr; | ||||
286 | } | ||||
287 | |||||
288 | 2 | 198µs | my $handler = $this->getHandler( $topicObject, $attachment ); # spent 198µs making 2 calls to Foswiki::Store::RcsWrap::getHandler, avg 99µs/call | ||
289 | 2 | 620ms | return $handler->getRevisionHistory(); # spent 620ms making 2 calls to Foswiki::Store::VC::Handler::getRevisionHistory, avg 310ms/call | ||
290 | } | ||||
291 | |||||
292 | sub getNextRevision { | ||||
293 | my ( $this, $topicObject ) = @_; | ||||
294 | my $handler = $this->getHandler($topicObject); | ||||
295 | return $handler->getNextRevisionID(); | ||||
296 | } | ||||
297 | |||||
298 | sub getRevisionDiff { | ||||
299 | my ( $this, $topicObject, $rev2, $contextLines ) = @_; | ||||
300 | ASSERT( defined($contextLines) ) if DEBUG; | ||||
301 | |||||
302 | my $rcs = $this->getHandler($topicObject); | ||||
303 | return $rcs->revisionDiff( $topicObject->getLoadedRev(), $rev2, | ||||
304 | $contextLines ); | ||||
305 | } | ||||
306 | |||||
307 | sub getAttachmentVersionInfo { | ||||
308 | my ( $this, $topicObject, $rev, $attachment ) = @_; | ||||
309 | my $handler = $this->getHandler( $topicObject, $attachment ); | ||||
310 | return $handler->getInfo( $rev || 0 ); | ||||
311 | } | ||||
312 | |||||
313 | sub getVersionInfo { | ||||
314 | my ( $this, $topicObject ) = @_; | ||||
315 | my $info = $this->askListenersVersionInfo($topicObject); | ||||
316 | |||||
317 | if ( not defined $info ) { | ||||
318 | my $handler = $this->getHandler($topicObject); | ||||
319 | |||||
320 | $info = $handler->getInfo( $topicObject->getLoadedRev() ); | ||||
321 | } | ||||
322 | |||||
323 | return $info; | ||||
324 | } | ||||
325 | |||||
326 | sub saveAttachment { | ||||
327 | my ( $this, $topicObject, $name, $stream, $cUID, $comment ) = @_; | ||||
328 | my $handler = $this->getHandler( $topicObject, $name ); | ||||
329 | my $currentRev = $handler->getLatestRevisionID(); | ||||
330 | my $nextRev = $currentRev + 1; | ||||
331 | my $verb = ( $topicObject->hasAttachment($name) ) ? 'update' : 'insert'; | ||||
332 | $handler->addRevisionFromStream( $stream, $comment, $cUID ); | ||||
333 | $this->tellListeners( | ||||
334 | verb => $verb, | ||||
335 | newmeta => $topicObject, | ||||
336 | newattachment => $name | ||||
337 | ); | ||||
338 | $handler->recordChange( $cUID, $nextRev ); | ||||
339 | return $nextRev; | ||||
340 | } | ||||
341 | |||||
342 | sub saveTopic { | ||||
343 | my ( $this, $topicObject, $cUID, $options ) = @_; | ||||
344 | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; | ||||
345 | ASSERT($cUID) if DEBUG; | ||||
346 | |||||
347 | my $handler = $this->getHandler($topicObject); | ||||
348 | |||||
349 | my $verb = ( $topicObject->existsInStore() ) ? 'update' : 'insert'; | ||||
350 | |||||
351 | # just in case they are not sequential | ||||
352 | my $nextRev = $handler->getNextRevisionID(); | ||||
353 | my $ti = $topicObject->get('TOPICINFO'); | ||||
354 | $ti->{version} = $nextRev; | ||||
355 | $ti->{author} = $cUID; | ||||
356 | |||||
357 | $handler->addRevisionFromText( $topicObject->getEmbeddedStoreForm(), | ||||
358 | 'save topic', $cUID, $options->{forcedate} ); | ||||
359 | |||||
360 | my $extra = $options->{minor} ? 'minor' : ''; | ||||
361 | $handler->recordChange( $cUID, $nextRev, $extra ); | ||||
362 | |||||
363 | $this->tellListeners( verb => $verb, newmeta => $topicObject ); | ||||
364 | |||||
365 | return $nextRev; | ||||
366 | } | ||||
367 | |||||
368 | sub repRev { | ||||
369 | my ( $this, $topicObject, $cUID, %options ) = @_; | ||||
370 | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; | ||||
371 | ASSERT($cUID) if DEBUG; | ||||
372 | my $info = $topicObject->getRevisionInfo(); | ||||
373 | my $handler = $this->getHandler($topicObject); | ||||
374 | $handler->replaceRevision( $topicObject->getEmbeddedStoreForm(), | ||||
375 | 'reprev', $cUID, $info->{date} ); | ||||
376 | my $rev = $handler->getLatestRevisionID(); | ||||
377 | $handler->recordChange( $cUID, $rev, 'minor, reprev' ); | ||||
378 | |||||
379 | $this->tellListeners( verb => 'update', newmeta => $topicObject ); | ||||
380 | |||||
381 | return $rev; | ||||
382 | } | ||||
383 | |||||
384 | sub delRev { | ||||
385 | my ( $this, $topicObject, $cUID ) = @_; | ||||
386 | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; | ||||
387 | ASSERT($cUID) if DEBUG; | ||||
388 | |||||
389 | my $handler = $this->getHandler($topicObject); | ||||
390 | my $rev = $handler->getLatestRevisionID(); | ||||
391 | if ( $rev <= 1 ) { | ||||
392 | throw Error::Simple( 'Cannot delete initial revision of ' | ||||
393 | . $topicObject->web . '.' | ||||
394 | . $topicObject->topic ); | ||||
395 | } | ||||
396 | $handler->deleteRevision(); | ||||
397 | |||||
398 | # restore last topic from repository | ||||
399 | $handler->restoreLatestRevision($cUID); | ||||
400 | |||||
401 | # reload the topic object | ||||
402 | $topicObject->unload(); | ||||
403 | $topicObject->loadVersion(); | ||||
404 | |||||
405 | $this->tellListeners( verb => 'update', newmeta => $topicObject ); | ||||
406 | |||||
407 | $handler->recordChange( $cUID, $rev ); | ||||
408 | |||||
409 | return $rev; | ||||
410 | } | ||||
411 | |||||
412 | sub atomicLockInfo { | ||||
413 | my ( $this, $topicObject ) = @_; | ||||
414 | my $handler = $this->getHandler($topicObject); | ||||
415 | return $handler->isLocked(); | ||||
416 | } | ||||
417 | |||||
418 | # It would be nice to use flock to do this, but the API is unreliable | ||||
419 | # (doesn't work on all platforms) | ||||
420 | sub atomicLock { | ||||
421 | my ( $this, $topicObject, $cUID ) = @_; | ||||
422 | my $handler = $this->getHandler($topicObject); | ||||
423 | $handler->setLock( 1, $cUID ); | ||||
424 | } | ||||
425 | |||||
426 | sub atomicUnlock { | ||||
427 | my ( $this, $topicObject, $cUID ) = @_; | ||||
428 | |||||
429 | my $handler = $this->getHandler($topicObject); | ||||
430 | $handler->setLock( 0, $cUID ); | ||||
431 | } | ||||
432 | |||||
433 | # A web _has_ to have a preferences topic to be a web. | ||||
434 | # spent 348µs (80+268) within Foswiki::Store::VC::Store::webExists which was called 2 times, avg 174µs/call:
# 2 times (80µs+268µs) by Foswiki::webExists at line 3657 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm, avg 174µs/call | ||||
435 | 12 | 85µs | my ( $this, $web ) = @_; | ||
436 | |||||
437 | return 0 unless defined $web; | ||||
438 | 2 | 9µs | $web =~ s#\.#/#go; # spent 9µs making 2 calls to Foswiki::Store::VC::Store::CORE:subst, avg 4µs/call | ||
439 | |||||
440 | # Foswiki ships with TWikiCompatibilityPlugin but if it is disabled we | ||||
441 | # do not want the TWiki web to appear as a valid web to anyone. | ||||
442 | if ( $web eq 'TWiki' ) { | ||||
443 | unless ( exists $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin} | ||||
444 | && defined $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{Enabled} | ||||
445 | && $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{Enabled} == 1 ) | ||||
446 | { | ||||
447 | return 0; | ||||
448 | } | ||||
449 | } | ||||
450 | |||||
451 | 2 | 199µs | my $handler = $this->getHandler( $web, $Foswiki::cfg{WebPrefsTopicName} ); # spent 199µs making 2 calls to Foswiki::Store::RcsWrap::getHandler, avg 99µs/call | ||
452 | 2 | 61µs | return $handler->storedDataExists(); # spent 61µs making 2 calls to Foswiki::Store::VC::Handler::storedDataExists, avg 30µs/call | ||
453 | } | ||||
454 | |||||
455 | # spent 35.6ms (7.21+28.4) within Foswiki::Store::VC::Store::topicExists which was called 272 times, avg 131µs/call:
# 155 times (3.89ms+16.4ms) by Foswiki::topicExists at line 3674 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm, avg 131µs/call
# 97 times (2.76ms+9.87ms) by Foswiki::Meta::existsInStore at line 703 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm, avg 130µs/call
# 20 times (561µs+2.13ms) by Foswiki::INCLUDE at line 182 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Macros/INCLUDE.pm, avg 135µs/call | ||||
456 | 1632 | 7.57ms | my ( $this, $web, $topic ) = @_; | ||
457 | |||||
458 | return 0 unless defined $web && $web ne ''; | ||||
459 | 272 | 540µs | $web =~ s#\.#/#go; # spent 540µs making 272 calls to Foswiki::Store::VC::Store::CORE:subst, avg 2µs/call | ||
460 | return 0 unless defined $topic && $topic ne ''; | ||||
461 | |||||
462 | 272 | 22.7ms | my $handler = $this->getHandler( $web, $topic ); # spent 22.7ms making 272 calls to Foswiki::Store::RcsWrap::getHandler, avg 83µs/call | ||
463 | 272 | 5.13ms | return $handler->storedDataExists(); # spent 5.13ms making 272 calls to Foswiki::Store::VC::Handler::storedDataExists, avg 19µs/call | ||
464 | } | ||||
465 | |||||
466 | sub getApproxRevTime { | ||||
467 | my ( $this, $web, $topic ) = @_; | ||||
468 | |||||
469 | my $handler = $this->getHandler( $web, $topic ); | ||||
470 | return $handler->getLatestRevisionTime(); | ||||
471 | } | ||||
472 | |||||
473 | sub eachChange { | ||||
474 | my ( $this, $webObject, $time ) = @_; | ||||
475 | |||||
476 | my $handler = $this->getHandler($webObject); | ||||
477 | return $handler->eachChange($time); | ||||
478 | } | ||||
479 | |||||
480 | sub eachAttachment { | ||||
481 | my ( $this, $topicObject ) = @_; | ||||
482 | |||||
483 | my $handler = $this->getHandler($topicObject); | ||||
484 | my @list = $handler->getAttachmentList(); | ||||
485 | require Foswiki::ListIterator; | ||||
486 | return new Foswiki::ListIterator( \@list ); | ||||
487 | } | ||||
488 | |||||
489 | # spent 170ms (753µs+169) within Foswiki::Store::VC::Store::eachTopic which was called:
# once (753µs+169ms) by Foswiki::Meta::eachTopic at line 950 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm | ||||
490 | 5 | 745µs | my ( $this, $webObject ) = @_; | ||
491 | |||||
492 | 1 | 178µs | my $handler = $this->getHandler($webObject); # spent 178µs making 1 call to Foswiki::Store::RcsWrap::getHandler | ||
493 | 1 | 169ms | my @list = $handler->getTopicNames(); # spent 169ms making 1 call to Foswiki::Store::VC::Handler::getTopicNames | ||
494 | |||||
495 | require Foswiki::ListIterator; | ||||
496 | 1 | 52µs | return new Foswiki::ListIterator( \@list ); # spent 52µs making 1 call to Foswiki::ListIterator::new | ||
497 | } | ||||
498 | |||||
499 | sub eachWeb { | ||||
500 | my ( $this, $webObject, $all ) = @_; | ||||
501 | |||||
502 | # Undocumented; this fn actually accepts a web name as well. This is | ||||
503 | # to make the recursion more efficient. | ||||
504 | my $web = ref($webObject) ? $webObject->web : $webObject; | ||||
505 | |||||
506 | my $handler = $this->getHandler($web); | ||||
507 | my @list = $handler->getWebNames(); | ||||
508 | if ($all) { | ||||
509 | my $root = $web ? "$web/" : ''; | ||||
510 | my @expandedList; | ||||
511 | while ( my $wp = shift(@list) ) { | ||||
512 | push( @expandedList, $wp ); | ||||
513 | my $it = $this->eachWeb( $root . $wp, $all ); | ||||
514 | push( @expandedList, map { "$wp/$_" } $it->all() ); | ||||
515 | } | ||||
516 | @list = @expandedList; | ||||
517 | } | ||||
518 | @list = sort(@list); | ||||
519 | require Foswiki::ListIterator; | ||||
520 | return new Foswiki::ListIterator( \@list ); | ||||
521 | } | ||||
522 | |||||
523 | sub remove { | ||||
524 | my ( $this, $cUID, $topicObject, $attachment ) = @_; | ||||
525 | ASSERT( $topicObject->web ) if DEBUG; | ||||
526 | |||||
527 | my $handler = $this->getHandler( $topicObject, $attachment ); | ||||
528 | $handler->remove(); | ||||
529 | |||||
530 | $this->tellListeners( | ||||
531 | verb => 'remove', | ||||
532 | oldmeta => $topicObject, | ||||
533 | oldattachment => $attachment | ||||
534 | ); | ||||
535 | |||||
536 | # Only log when deleting topics or attachment, otherwise we would re-create | ||||
537 | # an empty directory with just a .changes. See Item9278 | ||||
538 | if ( my $topic = $topicObject->topic ) { | ||||
539 | $handler->recordChange( $cUID, 0, 'Deleted ' . $topic ); | ||||
540 | } | ||||
541 | elsif ($attachment) { | ||||
542 | $handler->recordChange( $cUID, 0, 'Deleted attachment ' . $attachment ); | ||||
543 | } | ||||
544 | } | ||||
545 | |||||
546 | # spent 1.66s (2.20ms+1.66) within Foswiki::Store::VC::Store::query which was called:
# once (2.20ms+1.66s) by Foswiki::Meta::query at line 907 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm | ||||
547 | 10 | 144µs | my ( $this, $query, $inputTopicSet, $session, $options ) = @_; | ||
548 | |||||
549 | my $engine; | ||||
550 | 1 | 3µs | if ( $query->isa('Foswiki::Query::Node') ) { # spent 3µs making 1 call to UNIVERSAL::isa | ||
551 | unless ( $this->{queryObj} ) { | ||||
552 | my $module = $Foswiki::cfg{Store}{QueryAlgorithm}; | ||||
553 | eval "require $module"; # spent 292µs executing statements in string eval | ||||
554 | die | ||||
555 | "Bad {Store}{QueryAlgorithm}; suggest you run configure and select a different algorithm\n$@" | ||||
556 | if $@; | ||||
557 | 1 | 55µs | $this->{queryObj} = $module->new(); # spent 55µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::new | ||
558 | } | ||||
559 | $engine = $this->{queryObj}; | ||||
560 | } | ||||
561 | else { | ||||
562 | ASSERT( $query->isa('Foswiki::Search::Node') ) if DEBUG; | ||||
563 | unless ( $this->{searchQueryObj} ) { | ||||
564 | my $module = $Foswiki::cfg{Store}{SearchAlgorithm}; | ||||
565 | eval "require $module"; | ||||
566 | die | ||||
567 | "Bad {Store}{SearchAlgorithm}; suggest you run configure and select a different algorithm\n$@" | ||||
568 | if $@; | ||||
569 | $this->{searchQueryObj} = $module->new(); | ||||
570 | } | ||||
571 | $engine = $this->{searchQueryObj}; | ||||
572 | } | ||||
573 | |||||
574 | 2 | 68µs | 2 | 91µs | # spent 54µs (18+37) within Foswiki::Store::VC::Store::BEGIN@574 which was called:
# once (18µs+37µs) by Foswiki::Store::RcsWrap::BEGIN@23 at line 574 # spent 54µs making 1 call to Foswiki::Store::VC::Store::BEGIN@574
# spent 37µs making 1 call to strict::unimport |
575 | 1 | 1.65s | return $engine->query( $query, $inputTopicSet, $session, $options ); # spent 1.65s making 1 call to Foswiki::Store::Interfaces::QueryAlgorithm::query | ||
576 | 2 | 306µs | 2 | 75µs | # spent 45µs (16+30) within Foswiki::Store::VC::Store::BEGIN@576 which was called:
# once (16µs+30µs) by Foswiki::Store::RcsWrap::BEGIN@23 at line 576 # spent 45µs making 1 call to Foswiki::Store::VC::Store::BEGIN@576
# spent 30µs making 1 call to strict::import |
577 | } | ||||
578 | |||||
579 | sub getRevisionAtTime { | ||||
580 | my ( $this, $topicObject, $time ) = @_; | ||||
581 | |||||
582 | my $handler = $this->getHandler($topicObject); | ||||
583 | return $handler->getRevisionAtTime($time); | ||||
584 | } | ||||
585 | |||||
586 | sub getLease { | ||||
587 | my ( $this, $topicObject ) = @_; | ||||
588 | |||||
589 | my $handler = $this->getHandler($topicObject); | ||||
590 | my $lease = $handler->getLease(); | ||||
591 | return $lease; | ||||
592 | } | ||||
593 | |||||
594 | sub setLease { | ||||
595 | my ( $this, $topicObject, $lease ) = @_; | ||||
596 | |||||
597 | my $handler = $this->getHandler($topicObject); | ||||
598 | $handler->setLease($lease); | ||||
599 | } | ||||
600 | |||||
601 | sub removeSpuriousLeases { | ||||
602 | my ( $this, $web ) = @_; | ||||
603 | my $handler = $this->getHandler($web); | ||||
604 | $handler->removeSpuriousLeases(); | ||||
605 | } | ||||
606 | |||||
607 | 1 | 6µs | 1; | ||
608 | __END__ | ||||
# spent 2µs within Foswiki::Store::VC::Store::CORE:match which was called:
# once (2µs+0s) by Foswiki::Store::VC::Store::readTopic at line 93 | |||||
# spent 1.09ms within Foswiki::Store::VC::Store::CORE:subst which was called 344 times, avg 3µs/call:
# 272 times (540µs+0s) by Foswiki::Store::VC::Store::topicExists at line 459, avg 2µs/call
# 70 times (536µs+0s) by Foswiki::Store::VC::Store::readTopic at line 109, avg 8µs/call
# 2 times (9µs+0s) by Foswiki::Store::VC::Store::webExists at line 438, avg 4µs/call |