Completed
Push — master ( c2790d...17b03f )
by Alexander
03:12
created

RepositoryFiller::getPathChecksum()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * This file is part of the SVN-Buddy library.
4
 * For the full copyright and license information, please view
5
 * the LICENSE file that was distributed with this source code.
6
 *
7
 * @copyright Alexander Obuhovich <[email protected]>
8
 * @link      https://github.com/console-helpers/svn-buddy
9
 */
10
11
namespace ConsoleHelpers\SVNBuddy\Repository\RevisionLog;
12
13
14
use Aura\Sql\ExtendedPdoInterface;
15
use ConsoleHelpers\SVNBuddy\Database\DatabaseCache;
16
use ConsoleHelpers\SVNBuddy\Database\StatementProfiler;
17
18
class RepositoryFiller
19
{
20
21
	/**
22
	 * Database.
23
	 *
24
	 * @var ExtendedPdoInterface
25
	 */
26
	protected $database;
27
28
	/**
29
	 * Database cache.
30
	 *
31
	 * @var DatabaseCache
32
	 */
33
	protected $databaseCache;
34
35
	/**
36
	 * RepositoryFiller constructor.
37
	 *
38
	 * @param ExtendedPdoInterface $database       Database.
39
	 * @param DatabaseCache        $database_cache Database cache.
40
	 */
41 135
	public function __construct(ExtendedPdoInterface $database, DatabaseCache $database_cache)
42
	{
43 135
		$this->database = $database;
44 135
		$this->databaseCache = $database_cache;
45
46 135
		$this->databaseCache->cacheTable('Paths');
47 135
	}
48
49
	/**
50
	 * Creates a project.
51
	 *
52
	 * @param string      $path       Path.
53
	 * @param integer     $is_deleted Is Deleted.
54
	 * @param string|null $bug_regexp Bug regexp.
55
	 *
56
	 * @return integer
57
	 */
58 57
	public function addProject($path, $is_deleted = 0, $bug_regexp = null)
59
	{
60
		$sql = 'INSERT INTO Projects (Path, IsDeleted, BugRegExp)
61 57
				VALUES (:path, :is_deleted, :bug_regexp)';
62 57
		$this->database->perform($sql, array(
63 57
			'path' => $path,
64 57
			'is_deleted' => $is_deleted,
65 57
			'bug_regexp' => $bug_regexp,
66 57
		));
67
68 57
		$project_id = $this->database->lastInsertId();
69
70
		// There are no "0" revision in repository, but we need to bind project path to some revision.
71 57
		if ( $path === '/' ) {
72 3
			$this->addPath($path, '', $path, 0);
73 3
		}
74
75 57
		return $project_id;
76
	}
77
78
	/**
79
	 * Changes project status.
80
	 *
81
	 * @param integer $project_id Project ID.
82
	 * @param integer $is_deleted Is deleted flag.
83
	 *
84
	 * @return void
85
	 */
86 7
	public function setProjectStatus($project_id, $is_deleted)
87
	{
88
		$sql = 'UPDATE Projects
89
				SET IsDeleted = :is_deleted
90 7
				WHERE Id = :id';
91 7
		$this->database->perform($sql, array(
92 7
			'is_deleted' => (int)$is_deleted,
93 7
			'id' => $project_id,
94 7
		));
95 7
	}
96
97
	/**
98
	 * Changes project bug regexp.
99
	 *
100
	 * @param integer     $project_id Project ID.
101
	 * @param string|null $bug_regexp Bug regexp.
102
	 *
103
	 * @return void
104
	 */
105 9
	public function setProjectBugRegexp($project_id, $bug_regexp)
106
	{
107
		$sql = 'UPDATE Projects
108
				SET BugRegExp = :bug_regexp
109 9
				WHERE Id = :id';
110 9
		$this->database->perform($sql, array(
111 9
			'bug_regexp' => $bug_regexp,
112 9
			'id' => $project_id,
113 9
		));
114 9
	}
115
116
	/**
117
	 * Adds commit.
118
	 *
119
	 * @param integer $revision Revision.
120
	 * @param string  $author   Author.
121
	 * @param integer $date     Date.
122
	 * @param string  $message  Message.
123
	 *
124
	 * @return void
125
	 */
126 52
	public function addCommit($revision, $author, $date, $message)
127
	{
128
		$sql = 'INSERT INTO Commits (Revision, Author, Date, Message)
129 52
				VALUES (:revision, :author, :date, :message)';
130 52
		$this->database->perform($sql, array(
131 52
			'revision' => $revision,
132 52
			'author' => $author,
133 52
			'date' => $date,
134 52
			'message' => $message,
135 52
		));
136 52
	}
137
138
	/**
139
	 * Adds commit to project.
140
	 *
141
	 * @param integer $revision   Revision.
142
	 * @param integer $project_id Project ID.
143
	 *
144
	 * @return void
145
	 */
146 47
	public function addCommitToProject($revision, $project_id)
147
	{
148
		$sql = 'INSERT INTO CommitProjects (ProjectId, Revision)
149 47
				VALUES (:project_id, :revision)';
150 47
		$this->database->perform($sql, array('project_id' => $project_id, 'revision' => $revision));
151 47
	}
152
153
	/**
154
	 * Adds path.
155
	 *
156
	 * @param string  $path         Path.
157
	 * @param string  $ref          Ref.
158
	 * @param string  $project_path Project path.
159
	 * @param integer $revision     Revision.
160
	 *
161
	 * @return integer
162
	 */
163 67
	public function addPath($path, $ref, $project_path, $revision)
164
	{
165
		$sql = 'INSERT INTO Paths (
166
					Path, PathNestingLevel, PathHash, RefName, ProjectPath, RevisionAdded, RevisionLastSeen
167
				)
168 67
				VALUES (:path, :path_nesting_level, :path_hash, :ref, :project_path, :revision, :revision)';
169 67
		$this->database->perform($sql, array(
170 67
			'path' => $path,
171 67
			'path_nesting_level' => substr_count($path, '/') - 1,
172 67
			'path_hash' => $this->getPathChecksum($path),
173 67
			'ref' => $ref,
174 67
			'project_path' => $project_path,
175 67
			'revision' => $revision,
176 67
		));
177 67
		$path_id = $this->database->lastInsertId();
178
179 67
		$this->propagateRevisionLastSeen($path, $revision);
180
181 67
		return $path_id;
182
	}
183
184
	/**
185
	 * Adds path to commit.
186
	 *
187
	 * @param integer      $revision      Revision.
188
	 * @param string       $action        Action.
189
	 * @param string       $kind          Kind.
190
	 * @param integer      $path_id       Path ID.
191
	 * @param integer|null $copy_revision Copy revision.
192
	 * @param integer|null $copy_path_id  Copy path ID.
193
	 *
194
	 * @return void
195
	 */
196 60
	public function addPathToCommit($revision, $action, $kind, $path_id, $copy_revision = null, $copy_path_id = null)
197
	{
198
		$sql = 'INSERT INTO CommitPaths (Revision, Action, Kind, PathId, CopyRevision, CopyPathId)
199 60
				VALUES (:revision, :action, :kind, :path_id, :copy_revision, :copy_path_id)';
200 60
		$this->database->perform($sql, array(
201 60
			'revision' => $revision,
202 60
			'action' => $action,
203 60
			'kind' => $kind,
204 60
			'path_id' => $path_id,
205 60
			'copy_revision' => $copy_revision,
206 60
			'copy_path_id' => $copy_path_id,
207 60
		));
208 60
	}
209
210
	/**
211
	 * Touches given path.
212
	 *
213
	 * @param string  $path        Path.
214
	 * @param integer $revision    Revision.
215
	 * @param array   $fields_hash Fields hash.
216
	 *
217
	 * @return array
218
	 * @throws \InvalidArgumentException When "$fields_hash" is empty.
219
	 */
220 22
	public function touchPath($path, $revision, array $fields_hash)
221
	{
222 22
		if ( !$fields_hash ) {
223 1
			throw new \InvalidArgumentException('The "$fields_hash" variable can\'t be empty.');
224
		}
225
226 21
		$path_hash = $this->getPathChecksum($path);
227 21
		$to_update = $this->propagateRevisionLastSeen($path, $revision);
228 21
		$to_update[$path_hash] = $fields_hash;
229
230 21
		$bind_params = array_values($fields_hash);
231 21
		$bind_params[] = $path_hash;
232
233
		$sql = 'UPDATE Paths
234 21
				SET ' . implode(' = ?, ', array_keys($fields_hash)) . ' = ?
235 21
				WHERE PathHash = ?';
236 21
		$this->database->perform($sql, $bind_params);
237
238 21
		return $to_update;
239
	}
240
241
	/**
242
	 * Propagates revision last seen.
243
	 *
244
	 * @param string $path     Path.
245
	 * @param string $revision Revision.
246
	 *
247
	 * @return array
248
	 */
249 67
	protected function propagateRevisionLastSeen($path, $revision)
250
	{
251 67
		$to_update = array();
252 67
		$update_path = $path;
253
254 67
		$select_sql = 'SELECT RevisionLastSeen FROM Paths WHERE PathHash = :path_hash';
255 67
		$update_sql = 'UPDATE Paths SET RevisionLastSeen = :revision WHERE PathHash = :path_hash';
256
257 67
		while ( ($update_path = dirname($update_path) . '/') !== '//' ) {
258 62
			$update_path_hash = $this->getPathChecksum($update_path);
259
260 62
			$fields_hash = $this->databaseCache->getFromCache(
261 62
				'Paths',
262 62
				$update_path_hash . '/' . __METHOD__,
263 62
				$select_sql,
264 62
				array('path_hash' => $update_path_hash)
265 62
			);
266
267
			// Missing parent path. Can happen for example, when repository was created via "cvs2svn".
268 62
			if ( $fields_hash === false ) {
269
				/** @var StatementProfiler $profiler */
270 49
				$profiler = $this->database->getProfiler();
271 49
				$profiler->removeProfile($select_sql, array('path_hash' => $update_path_hash));
272 49
				break;
273
			}
274
275
			// TODO: Collect these paths and issue single update after cycle finishes.
276 22
			if ( (int)$fields_hash['RevisionLastSeen'] < $revision ) {
277 15
				$this->database->perform(
278 15
					$update_sql,
279 15
					array('revision' => $revision, 'path_hash' => $update_path_hash)
280 15
				);
281
282 15
				$fields_hash = array('RevisionLastSeen' => $revision);
283 15
				$this->databaseCache->setIntoCache('Paths', $update_path_hash . '/' . __METHOD__, $fields_hash);
284 15
				$to_update[$update_path_hash] = $fields_hash;
285 15
			}
286 22
		};
287
288 67
		return $to_update;
289
	}
290
291
	/**
292
	 * Returns fields, that needs to be changed for given path.
293
	 *
294
	 * @param string  $action    Action.
295
	 * @param integer $revision  Revision.
296
	 * @param array   $path_data Path data.
297
	 *
298
	 * @return array
299
	 */
300 27
	public function getPathTouchFields($action, $revision, array $path_data)
301
	{
302 27
		$fields_hash = array();
303
304 27
		if ( $action === 'D' ) {
305 6
			$fields_hash['RevisionDeleted'] = $revision;
306 6
		}
307
		else {
308 24
			if ( $path_data['RevisionDeleted'] > 0 ) {
309 4
				$fields_hash['RevisionDeleted'] = null;
310 4
			}
311
312 24
			if ( $action === 'A' && $path_data['RevisionAdded'] > $revision ) {
313 2
				$fields_hash['RevisionAdded'] = $revision;
314 2
			}
315
316 24
			if ( $path_data['RevisionLastSeen'] < $revision ) {
317 21
				$fields_hash['RevisionLastSeen'] = $revision;
318 21
			}
319
		}
320
321 27
		return $fields_hash;
322
	}
323
324
	/**
325
	 * Sets project path for given paths.
326
	 *
327
	 * @param array  $path_ids     Path IDs.
328
	 * @param string $project_path Project path.
329
	 *
330
	 * @return void
331
	 */
332 4
	public function movePathsIntoProject(array $path_ids, $project_path)
333
	{
334
		$sql = 'UPDATE Paths
335
				SET ProjectPath = :path
336 4
				WHERE Id IN (:path_ids)';
337 4
		$this->database->perform($sql, array(
338 4
			'path' => $project_path,
339 4
			'path_ids' => $path_ids,
340 4
		));
341 4
	}
342
343
	/**
344
	 * Adds commit with bugs.
345
	 *
346
	 * @param array   $bugs     Bugs.
347
	 * @param integer $revision Revision.
348
	 *
349
	 * @return void
350
	 */
351 44
	public function addBugsToCommit(array $bugs, $revision)
352
	{
353 44
		foreach ( $bugs as $bug ) {
354
			$sql = 'INSERT INTO CommitBugs (Revision, Bug)
355 8
					VALUES (:revision, :bug)';
356 8
			$this->database->perform($sql, array(
357 8
				'revision' => $revision,
358 8
				'bug' => $bug,
359 8
			));
360 44
		}
361 44
	}
362
363
	/**
364
	 * Adds merge commit.
365
	 *
366
	 * @param integer $revision         Revision.
367
	 * @param array   $merged_revisions Merged revisions.
368
	 *
369
	 * @return void
370
	 */
371 46
	public function addMergeCommit($revision, array $merged_revisions)
372
	{
373 46
		foreach ( $merged_revisions as $merged_revision ) {
374
			$sql = 'INSERT INTO Merges (MergeRevision, MergedRevision)
375 9
					VALUES (:merge_revision, :merged_revision)';
376 9
			$this->database->perform($sql, array(
377 9
				'merge_revision' => $revision,
378 9
				'merged_revision' => $merged_revision,
379 9
			));
380 46
		}
381 46
	}
382
383
	/**
384
	 * Adds ref to project.
385
	 *
386
	 * @param string  $ref        Ref.
387
	 * @param integer $project_id Project ID.
388
	 *
389
	 * @return integer
390
	 */
391 25
	public function addRefToProject($ref, $project_id)
392
	{
393
		$sql = 'INSERT INTO ProjectRefs (ProjectId, Name)
394 25
				VALUES (:project_id, :name)';
395 25
		$this->database->perform($sql, array('project_id' => $project_id, 'name' => $ref));
396
397 25
		return $this->database->lastInsertId();
398
	}
399
400
	/**
401
	 * Adds ref to commit and commit to project.
402
	 *
403
	 * @param integer $revision Revision.
404
	 * @param integer $ref_id   Ref ID.
405
	 *
406
	 * @return void
407
	 */
408 24
	public function addCommitToRef($revision, $ref_id)
409
	{
410
		$sql = 'INSERT INTO CommitRefs (Revision, RefId)
411 24
				VALUES (:revision, :ref_id)';
412 24
		$this->database->perform($sql, array('revision' => $revision, 'ref_id' => $ref_id));
413 24
	}
414
415
	/**
416
	 * Returns unsigned checksum of the path.
417
	 *
418
	 * @param string $path Path.
419
	 *
420
	 * @return integer
421
	 */
422 67
	public function getPathChecksum($path)
423
	{
424 67
		return sprintf('%u', crc32($path));
425
	}
426
427
}
428