Passed
Push — master ( 420a37...67ecdc )
by Morris
13:26 queued 12s
created
lib/private/Files/Cache/Cache.php 2 patches
Indentation   +940 added lines, -940 removed lines patch added patch discarded remove patch
@@ -62,944 +62,944 @@
 block discarded – undo
62 62
  * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater
63 63
  */
64 64
 class Cache implements ICache {
65
-	use MoveFromCacheTrait {
66
-		MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
67
-	}
68
-
69
-	/**
70
-	 * @var array partial data for the cache
71
-	 */
72
-	protected $partial = [];
73
-
74
-	/**
75
-	 * @var string
76
-	 */
77
-	protected $storageId;
78
-
79
-	private $storage;
80
-
81
-	/**
82
-	 * @var Storage $storageCache
83
-	 */
84
-	protected $storageCache;
85
-
86
-	/** @var IMimeTypeLoader */
87
-	protected $mimetypeLoader;
88
-
89
-	/**
90
-	 * @var IDBConnection
91
-	 */
92
-	protected $connection;
93
-
94
-	protected $eventDispatcher;
95
-
96
-	/** @var QuerySearchHelper */
97
-	protected $querySearchHelper;
98
-
99
-	/**
100
-	 * @param IStorage $storage
101
-	 */
102
-	public function __construct(IStorage $storage) {
103
-		$this->storageId = $storage->getId();
104
-		$this->storage = $storage;
105
-		if (strlen($this->storageId) > 64) {
106
-			$this->storageId = md5($this->storageId);
107
-		}
108
-
109
-		$this->storageCache = new Storage($storage);
110
-		$this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
111
-		$this->connection = \OC::$server->getDatabaseConnection();
112
-		$this->eventDispatcher = \OC::$server->getEventDispatcher();
113
-		$this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
114
-	}
115
-
116
-	private function getQueryBuilder() {
117
-		return new CacheQueryBuilder(
118
-			$this->connection,
119
-			\OC::$server->getSystemConfig(),
120
-			\OC::$server->getLogger(),
121
-			$this
122
-		);
123
-	}
124
-
125
-	/**
126
-	 * Get the numeric storage id for this cache's storage
127
-	 *
128
-	 * @return int
129
-	 */
130
-	public function getNumericStorageId() {
131
-		return $this->storageCache->getNumericId();
132
-	}
133
-
134
-	/**
135
-	 * get the stored metadata of a file or folder
136
-	 *
137
-	 * @param string | int $file either the path of a file or folder or the file id for a file or folder
138
-	 * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
139
-	 */
140
-	public function get($file) {
141
-		$query = $this->getQueryBuilder();
142
-		$query->selectFileCache();
143
-
144
-		if (is_string($file) or $file == '') {
145
-			// normalize file
146
-			$file = $this->normalize($file);
147
-
148
-			$query->whereStorageId()
149
-				->wherePath($file);
150
-		} else { //file id
151
-			$query->whereFileId($file);
152
-		}
153
-
154
-		$data = $query->execute()->fetch();
155
-
156
-		//merge partial data
157
-		if (!$data and is_string($file) and isset($this->partial[$file])) {
158
-			return $this->partial[$file];
159
-		} elseif (!$data) {
160
-			return $data;
161
-		} else {
162
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
163
-		}
164
-	}
165
-
166
-	/**
167
-	 * Create a CacheEntry from database row
168
-	 *
169
-	 * @param array $data
170
-	 * @param IMimeTypeLoader $mimetypeLoader
171
-	 * @return CacheEntry
172
-	 */
173
-	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
174
-		//fix types
175
-		$data['fileid'] = (int)$data['fileid'];
176
-		$data['parent'] = (int)$data['parent'];
177
-		$data['size'] = 0 + $data['size'];
178
-		$data['mtime'] = (int)$data['mtime'];
179
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
180
-		$data['encryptedVersion'] = (int)$data['encrypted'];
181
-		$data['encrypted'] = (bool)$data['encrypted'];
182
-		$data['storage_id'] = $data['storage'];
183
-		$data['storage'] = (int)$data['storage'];
184
-		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
185
-		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
186
-		if ($data['storage_mtime'] == 0) {
187
-			$data['storage_mtime'] = $data['mtime'];
188
-		}
189
-		$data['permissions'] = (int)$data['permissions'];
190
-		if (isset($data['creation_time'])) {
191
-			$data['creation_time'] = (int) $data['creation_time'];
192
-		}
193
-		if (isset($data['upload_time'])) {
194
-			$data['upload_time'] = (int) $data['upload_time'];
195
-		}
196
-		return new CacheEntry($data);
197
-	}
198
-
199
-	/**
200
-	 * get the metadata of all files stored in $folder
201
-	 *
202
-	 * @param string $folder
203
-	 * @return ICacheEntry[]
204
-	 */
205
-	public function getFolderContents($folder) {
206
-		$fileId = $this->getId($folder);
207
-		return $this->getFolderContentsById($fileId);
208
-	}
209
-
210
-	/**
211
-	 * get the metadata of all files stored in $folder
212
-	 *
213
-	 * @param int $fileId the file id of the folder
214
-	 * @return ICacheEntry[]
215
-	 */
216
-	public function getFolderContentsById($fileId) {
217
-		if ($fileId > -1) {
218
-			$query = $this->getQueryBuilder();
219
-			$query->selectFileCache()
220
-				->whereParent($fileId)
221
-				->orderBy('name', 'ASC');
222
-
223
-			$files = $query->execute()->fetchAll();
224
-			return array_map(function (array $data) {
225
-				return self::cacheEntryFromData($data, $this->mimetypeLoader);
226
-			}, $files);
227
-		}
228
-		return [];
229
-	}
230
-
231
-	/**
232
-	 * insert or update meta data for a file or folder
233
-	 *
234
-	 * @param string $file
235
-	 * @param array $data
236
-	 *
237
-	 * @return int file id
238
-	 * @throws \RuntimeException
239
-	 */
240
-	public function put($file, array $data) {
241
-		if (($id = $this->getId($file)) > -1) {
242
-			$this->update($id, $data);
243
-			return $id;
244
-		} else {
245
-			return $this->insert($file, $data);
246
-		}
247
-	}
248
-
249
-	/**
250
-	 * insert meta data for a new file or folder
251
-	 *
252
-	 * @param string $file
253
-	 * @param array $data
254
-	 *
255
-	 * @return int file id
256
-	 * @throws \RuntimeException
257
-	 *
258
-	 * @suppress SqlInjectionChecker
259
-	 */
260
-	public function insert($file, array $data) {
261
-		// normalize file
262
-		$file = $this->normalize($file);
263
-
264
-		if (isset($this->partial[$file])) { //add any saved partial data
265
-			$data = array_merge($this->partial[$file], $data);
266
-			unset($this->partial[$file]);
267
-		}
268
-
269
-		$requiredFields = ['size', 'mtime', 'mimetype'];
270
-		foreach ($requiredFields as $field) {
271
-			if (!isset($data[$field])) { //data not complete save as partial and return
272
-				$this->partial[$file] = $data;
273
-				return -1;
274
-			}
275
-		}
276
-
277
-		$data['path'] = $file;
278
-		if (!isset($data['parent'])) {
279
-			$data['parent'] = $this->getParentId($file);
280
-		}
281
-		$data['name'] = basename($file);
282
-
283
-		[$values, $extensionValues] = $this->normalizeData($data);
284
-		$values['storage'] = $this->getNumericStorageId();
285
-
286
-		try {
287
-			$builder = $this->connection->getQueryBuilder();
288
-			$builder->insert('filecache');
289
-
290
-			foreach ($values as $column => $value) {
291
-				$builder->setValue($column, $builder->createNamedParameter($value));
292
-			}
293
-
294
-			if ($builder->execute()) {
295
-				$fileId = $builder->getLastInsertId();
296
-
297
-				if (count($extensionValues)) {
298
-					$query = $this->getQueryBuilder();
299
-					$query->insert('filecache_extended');
300
-
301
-					$query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
302
-					foreach ($extensionValues as $column => $value) {
303
-						$query->setValue($column, $query->createNamedParameter($value));
304
-					}
305
-					$query->execute();
306
-				}
307
-
308
-				$this->eventDispatcher->dispatch(CacheInsertEvent::class, new CacheInsertEvent($this->storage, $file, $fileId));
309
-				return $fileId;
310
-			}
311
-		} catch (UniqueConstraintViolationException $e) {
312
-			// entry exists already
313
-			if ($this->connection->inTransaction()) {
314
-				$this->connection->commit();
315
-				$this->connection->beginTransaction();
316
-			}
317
-		}
318
-
319
-		// The file was created in the mean time
320
-		if (($id = $this->getId($file)) > -1) {
321
-			$this->update($id, $data);
322
-			return $id;
323
-		} else {
324
-			throw new \RuntimeException('File entry could not be inserted but could also not be selected with getId() in order to perform an update. Please try again.');
325
-		}
326
-	}
327
-
328
-	/**
329
-	 * update the metadata of an existing file or folder in the cache
330
-	 *
331
-	 * @param int $id the fileid of the existing file or folder
332
-	 * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
333
-	 */
334
-	public function update($id, array $data) {
335
-		if (isset($data['path'])) {
336
-			// normalize path
337
-			$data['path'] = $this->normalize($data['path']);
338
-		}
339
-
340
-		if (isset($data['name'])) {
341
-			// normalize path
342
-			$data['name'] = $this->normalize($data['name']);
343
-		}
344
-
345
-		[$values, $extensionValues] = $this->normalizeData($data);
346
-
347
-		if (count($values)) {
348
-			$query = $this->getQueryBuilder();
349
-
350
-			$query->update('filecache')
351
-				->whereFileId($id)
352
-				->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
353
-					return $query->expr()->orX(
354
-						$query->expr()->neq($key, $query->createNamedParameter($value)),
355
-						$query->expr()->isNull($key)
356
-					);
357
-				}, array_keys($values), array_values($values))));
358
-
359
-			foreach ($values as $key => $value) {
360
-				$query->set($key, $query->createNamedParameter($value));
361
-			}
362
-
363
-			$query->execute();
364
-		}
365
-
366
-		if (count($extensionValues)) {
367
-			try {
368
-				$query = $this->getQueryBuilder();
369
-				$query->insert('filecache_extended');
370
-
371
-				$query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
372
-				foreach ($extensionValues as $column => $value) {
373
-					$query->setValue($column, $query->createNamedParameter($value));
374
-				}
375
-
376
-				$query->execute();
377
-			} catch (UniqueConstraintViolationException $e) {
378
-				$query = $this->getQueryBuilder();
379
-				$query->update('filecache_extended')
380
-					->whereFileId($id)
381
-					->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
382
-						return $query->expr()->orX(
383
-							$query->expr()->neq($key, $query->createNamedParameter($value)),
384
-							$query->expr()->isNull($key)
385
-						);
386
-					}, array_keys($extensionValues), array_values($extensionValues))));
387
-
388
-				foreach ($extensionValues as $key => $value) {
389
-					$query->set($key, $query->createNamedParameter($value));
390
-				}
391
-
392
-				$query->execute();
393
-			}
394
-		}
395
-
396
-		$path = $this->getPathById($id);
397
-		// path can still be null if the file doesn't exist
398
-		if ($path !== null) {
399
-			$this->eventDispatcher->dispatch(CacheUpdateEvent::class, new CacheUpdateEvent($this->storage, $path, $id));
400
-		}
401
-	}
402
-
403
-	/**
404
-	 * extract query parts and params array from data array
405
-	 *
406
-	 * @param array $data
407
-	 * @return array
408
-	 */
409
-	protected function normalizeData(array $data): array {
410
-		$fields = [
411
-			'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
412
-			'etag', 'permissions', 'checksum', 'storage'];
413
-		$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
414
-
415
-		$doNotCopyStorageMTime = false;
416
-		if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
417
-			// this horrific magic tells it to not copy storage_mtime to mtime
418
-			unset($data['mtime']);
419
-			$doNotCopyStorageMTime = true;
420
-		}
421
-
422
-		$params = [];
423
-		$extensionParams = [];
424
-		foreach ($data as $name => $value) {
425
-			if (array_search($name, $fields) !== false) {
426
-				if ($name === 'path') {
427
-					$params['path_hash'] = md5($value);
428
-				} elseif ($name === 'mimetype') {
429
-					$params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
430
-					$value = $this->mimetypeLoader->getId($value);
431
-				} elseif ($name === 'storage_mtime') {
432
-					if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
433
-						$params['mtime'] = $value;
434
-					}
435
-				} elseif ($name === 'encrypted') {
436
-					if (isset($data['encryptedVersion'])) {
437
-						$value = $data['encryptedVersion'];
438
-					} else {
439
-						// Boolean to integer conversion
440
-						$value = $value ? 1 : 0;
441
-					}
442
-				}
443
-				$params[$name] = $value;
444
-			}
445
-			if (array_search($name, $extensionFields) !== false) {
446
-				$extensionParams[$name] = $value;
447
-			}
448
-		}
449
-		return [$params, array_filter($extensionParams)];
450
-	}
451
-
452
-	/**
453
-	 * get the file id for a file
454
-	 *
455
-	 * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
456
-	 *
457
-	 * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
458
-	 *
459
-	 * @param string $file
460
-	 * @return int
461
-	 */
462
-	public function getId($file) {
463
-		// normalize file
464
-		$file = $this->normalize($file);
465
-
466
-		$query = $this->getQueryBuilder();
467
-		$query->select('fileid')
468
-			->from('filecache')
469
-			->whereStorageId()
470
-			->wherePath($file);
471
-
472
-		$id = $query->execute()->fetchColumn();
473
-		return $id === false ? -1 : (int)$id;
474
-	}
475
-
476
-	/**
477
-	 * get the id of the parent folder of a file
478
-	 *
479
-	 * @param string $file
480
-	 * @return int
481
-	 */
482
-	public function getParentId($file) {
483
-		if ($file === '') {
484
-			return -1;
485
-		} else {
486
-			$parent = $this->getParentPath($file);
487
-			return (int)$this->getId($parent);
488
-		}
489
-	}
490
-
491
-	private function getParentPath($path) {
492
-		$parent = dirname($path);
493
-		if ($parent === '.') {
494
-			$parent = '';
495
-		}
496
-		return $parent;
497
-	}
498
-
499
-	/**
500
-	 * check if a file is available in the cache
501
-	 *
502
-	 * @param string $file
503
-	 * @return bool
504
-	 */
505
-	public function inCache($file) {
506
-		return $this->getId($file) != -1;
507
-	}
508
-
509
-	/**
510
-	 * remove a file or folder from the cache
511
-	 *
512
-	 * when removing a folder from the cache all files and folders inside the folder will be removed as well
513
-	 *
514
-	 * @param string $file
515
-	 */
516
-	public function remove($file) {
517
-		$entry = $this->get($file);
518
-
519
-		if ($entry) {
520
-			$query = $this->getQueryBuilder();
521
-			$query->delete('filecache')
522
-				->whereFileId($entry->getId());
523
-			$query->execute();
524
-
525
-			$query = $this->getQueryBuilder();
526
-			$query->delete('filecache_extended')
527
-				->whereFileId($entry->getId());
528
-			$query->execute();
529
-
530
-			if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
531
-				$this->removeChildren($entry);
532
-			}
533
-		}
534
-	}
535
-
536
-	/**
537
-	 * Get all sub folders of a folder
538
-	 *
539
-	 * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for
540
-	 * @return ICacheEntry[] the cache entries for the subfolders
541
-	 */
542
-	private function getSubFolders(ICacheEntry $entry) {
543
-		$children = $this->getFolderContentsById($entry->getId());
544
-		return array_filter($children, function ($child) {
545
-			return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
546
-		});
547
-	}
548
-
549
-	/**
550
-	 * Recursively remove all children of a folder
551
-	 *
552
-	 * @param ICacheEntry $entry the cache entry of the folder to remove the children of
553
-	 * @throws \OC\DatabaseException
554
-	 */
555
-	private function removeChildren(ICacheEntry $entry) {
556
-		$parentIds = [$entry->getId()];
557
-		$queue = [$entry->getId()];
558
-
559
-		// we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
560
-		// and collecting all folder ids to later use to delete the filecache entries
561
-		while ($entryId = array_pop($queue)) {
562
-			$children = $this->getFolderContentsById($entryId);
563
-			$childIds = array_map(function (ICacheEntry $cacheEntry) {
564
-				return $cacheEntry->getId();
565
-			}, $children);
566
-
567
-			$query = $this->getQueryBuilder();
568
-			$query->delete('filecache_extended')
569
-				->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY)));
570
-			$query->execute();
571
-
572
-			/** @var ICacheEntry[] $childFolders */
573
-			$childFolders = array_filter($children, function ($child) {
574
-				return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
575
-			});
576
-			foreach ($childFolders as $folder) {
577
-				$parentIds[] = $folder->getId();
578
-				$queue[] = $folder->getId();
579
-			}
580
-		}
581
-
582
-		$query = $this->getQueryBuilder();
583
-		$query->delete('filecache')
584
-			->whereParentIn($parentIds);
585
-		$query->execute();
586
-	}
587
-
588
-	/**
589
-	 * Move a file or folder in the cache
590
-	 *
591
-	 * @param string $source
592
-	 * @param string $target
593
-	 */
594
-	public function move($source, $target) {
595
-		$this->moveFromCache($this, $source, $target);
596
-	}
597
-
598
-	/**
599
-	 * Get the storage id and path needed for a move
600
-	 *
601
-	 * @param string $path
602
-	 * @return array [$storageId, $internalPath]
603
-	 */
604
-	protected function getMoveInfo($path) {
605
-		return [$this->getNumericStorageId(), $path];
606
-	}
607
-
608
-	/**
609
-	 * Move a file or folder in the cache
610
-	 *
611
-	 * @param \OCP\Files\Cache\ICache $sourceCache
612
-	 * @param string $sourcePath
613
-	 * @param string $targetPath
614
-	 * @throws \OC\DatabaseException
615
-	 * @throws \Exception if the given storages have an invalid id
616
-	 * @suppress SqlInjectionChecker
617
-	 */
618
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
619
-		if ($sourceCache instanceof Cache) {
620
-			// normalize source and target
621
-			$sourcePath = $this->normalize($sourcePath);
622
-			$targetPath = $this->normalize($targetPath);
623
-
624
-			$sourceData = $sourceCache->get($sourcePath);
625
-			$sourceId = $sourceData['fileid'];
626
-			$newParentId = $this->getParentId($targetPath);
627
-
628
-			[$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
629
-			[$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
630
-
631
-			if (is_null($sourceStorageId) || $sourceStorageId === false) {
632
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
633
-			}
634
-			if (is_null($targetStorageId) || $targetStorageId === false) {
635
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
636
-			}
637
-
638
-			$this->connection->beginTransaction();
639
-			if ($sourceData['mimetype'] === 'httpd/unix-directory') {
640
-				//update all child entries
641
-				$sourceLength = mb_strlen($sourcePath);
642
-				$query = $this->connection->getQueryBuilder();
643
-
644
-				$fun = $query->func();
645
-				$newPathFunction = $fun->concat(
646
-					$query->createNamedParameter($targetPath),
647
-					$fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
648
-				);
649
-				$query->update('filecache')
650
-					->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
651
-					->set('path_hash', $fun->md5($newPathFunction))
652
-					->set('path', $newPathFunction)
653
-					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
654
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
655
-
656
-				try {
657
-					$query->execute();
658
-				} catch (\OC\DatabaseException $e) {
659
-					$this->connection->rollBack();
660
-					throw $e;
661
-				}
662
-			}
663
-
664
-			$query = $this->getQueryBuilder();
665
-			$query->update('filecache')
666
-				->set('storage', $query->createNamedParameter($targetStorageId))
667
-				->set('path', $query->createNamedParameter($targetPath))
668
-				->set('path_hash', $query->createNamedParameter(md5($targetPath)))
669
-				->set('name', $query->createNamedParameter(basename($targetPath)))
670
-				->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
671
-				->whereFileId($sourceId);
672
-			$query->execute();
673
-
674
-			$this->connection->commit();
675
-		} else {
676
-			$this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
677
-		}
678
-	}
679
-
680
-	/**
681
-	 * remove all entries for files that are stored on the storage from the cache
682
-	 */
683
-	public function clear() {
684
-		$query = $this->getQueryBuilder();
685
-		$query->delete('filecache')
686
-			->whereStorageId();
687
-		$query->execute();
688
-
689
-		$query = $this->connection->getQueryBuilder();
690
-		$query->delete('storages')
691
-			->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
692
-		$query->execute();
693
-	}
694
-
695
-	/**
696
-	 * Get the scan status of a file
697
-	 *
698
-	 * - Cache::NOT_FOUND: File is not in the cache
699
-	 * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
700
-	 * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
701
-	 * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
702
-	 *
703
-	 * @param string $file
704
-	 *
705
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
706
-	 */
707
-	public function getStatus($file) {
708
-		// normalize file
709
-		$file = $this->normalize($file);
710
-
711
-		$query = $this->getQueryBuilder();
712
-		$query->select('size')
713
-			->from('filecache')
714
-			->whereStorageId()
715
-			->wherePath($file);
716
-		$size = $query->execute()->fetchColumn();
717
-		if ($size !== false) {
718
-			if ((int)$size === -1) {
719
-				return self::SHALLOW;
720
-			} else {
721
-				return self::COMPLETE;
722
-			}
723
-		} else {
724
-			if (isset($this->partial[$file])) {
725
-				return self::PARTIAL;
726
-			} else {
727
-				return self::NOT_FOUND;
728
-			}
729
-		}
730
-	}
731
-
732
-	/**
733
-	 * search for files matching $pattern
734
-	 *
735
-	 * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
736
-	 * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
737
-	 */
738
-	public function search($pattern) {
739
-		// normalize pattern
740
-		$pattern = $this->normalize($pattern);
741
-
742
-		if ($pattern === '%%') {
743
-			return [];
744
-		}
745
-
746
-		$query = $this->getQueryBuilder();
747
-		$query->selectFileCache()
748
-			->whereStorageId()
749
-			->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern)));
750
-
751
-		return array_map(function (array $data) {
752
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
753
-		}, $query->execute()->fetchAll());
754
-	}
755
-
756
-	/**
757
-	 * @param Statement $result
758
-	 * @return CacheEntry[]
759
-	 */
760
-	private function searchResultToCacheEntries(Statement $result) {
761
-		$files = $result->fetchAll();
762
-
763
-		return array_map(function (array $data) {
764
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
765
-		}, $files);
766
-	}
767
-
768
-	/**
769
-	 * search for files by mimetype
770
-	 *
771
-	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
772
-	 *        where it will search for all mimetypes in the group ('image/*')
773
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
774
-	 */
775
-	public function searchByMime($mimetype) {
776
-		$mimeId = $this->mimetypeLoader->getId($mimetype);
777
-
778
-		$query = $this->getQueryBuilder();
779
-		$query->selectFileCache()
780
-			->whereStorageId();
781
-
782
-		if (strpos($mimetype, '/')) {
783
-			$query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
784
-		} else {
785
-			$query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
786
-		}
787
-
788
-		return array_map(function (array $data) {
789
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
790
-		}, $query->execute()->fetchAll());
791
-	}
792
-
793
-	public function searchQuery(ISearchQuery $searchQuery) {
794
-		$builder = $this->getQueryBuilder();
795
-
796
-		$query = $builder->selectFileCache('file');
797
-
798
-		$query->whereStorageId();
799
-
800
-		if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
801
-			$query
802
-				->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
803
-				->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
804
-					$builder->expr()->eq('tagmap.type', 'tag.type'),
805
-					$builder->expr()->eq('tagmap.categoryid', 'tag.id')
806
-				))
807
-				->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
808
-				->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
809
-		}
810
-
811
-		$searchExpr = $this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation());
812
-		if ($searchExpr) {
813
-			$query->andWhere($searchExpr);
814
-		}
815
-
816
-		if ($searchQuery->limitToHome() && ($this instanceof HomeCache)) {
817
-			$query->andWhere($builder->expr()->like('path', $query->expr()->literal('files/%')));
818
-		}
819
-
820
-		$this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
821
-
822
-		if ($searchQuery->getLimit()) {
823
-			$query->setMaxResults($searchQuery->getLimit());
824
-		}
825
-		if ($searchQuery->getOffset()) {
826
-			$query->setFirstResult($searchQuery->getOffset());
827
-		}
828
-
829
-		$result = $query->execute();
830
-		return $this->searchResultToCacheEntries($result);
831
-	}
832
-
833
-	/**
834
-	 * Re-calculate the folder size and the size of all parent folders
835
-	 *
836
-	 * @param string|boolean $path
837
-	 * @param array $data (optional) meta data of the folder
838
-	 */
839
-	public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
840
-		$this->calculateFolderSize($path, $data);
841
-		if ($path !== '') {
842
-			$parent = dirname($path);
843
-			if ($parent === '.' or $parent === '/') {
844
-				$parent = '';
845
-			}
846
-			if ($isBackgroundScan) {
847
-				$parentData = $this->get($parent);
848
-				if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
849
-					$this->correctFolderSize($parent, $parentData, $isBackgroundScan);
850
-				}
851
-			} else {
852
-				$this->correctFolderSize($parent);
853
-			}
854
-		}
855
-	}
856
-
857
-	/**
858
-	 * get the incomplete count that shares parent $folder
859
-	 *
860
-	 * @param int $fileId the file id of the folder
861
-	 * @return int
862
-	 */
863
-	public function getIncompleteChildrenCount($fileId) {
864
-		if ($fileId > -1) {
865
-			$query = $this->getQueryBuilder();
866
-			$query->select($query->func()->count())
867
-				->from('filecache')
868
-				->whereParent($fileId)
869
-				->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
870
-
871
-			return (int)$query->execute()->fetchColumn();
872
-		}
873
-		return -1;
874
-	}
875
-
876
-	/**
877
-	 * calculate the size of a folder and set it in the cache
878
-	 *
879
-	 * @param string $path
880
-	 * @param array $entry (optional) meta data of the folder
881
-	 * @return int
882
-	 */
883
-	public function calculateFolderSize($path, $entry = null) {
884
-		$totalSize = 0;
885
-		if (is_null($entry) or !isset($entry['fileid'])) {
886
-			$entry = $this->get($path);
887
-		}
888
-		if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
889
-			$id = $entry['fileid'];
890
-
891
-			$query = $this->getQueryBuilder();
892
-			$query->selectAlias($query->func()->sum('size'), 'f1')
893
-				->selectAlias($query->func()->min('size'), 'f2')
894
-				->from('filecache')
895
-				->whereStorageId()
896
-				->whereParent($id);
897
-
898
-			if ($row = $query->execute()->fetch()) {
899
-				[$sum, $min] = array_values($row);
900
-				$sum = 0 + $sum;
901
-				$min = 0 + $min;
902
-				if ($min === -1) {
903
-					$totalSize = $min;
904
-				} else {
905
-					$totalSize = $sum;
906
-				}
907
-				if ($entry['size'] !== $totalSize) {
908
-					$this->update($id, ['size' => $totalSize]);
909
-				}
910
-			}
911
-		}
912
-		return $totalSize;
913
-	}
914
-
915
-	/**
916
-	 * get all file ids on the files on the storage
917
-	 *
918
-	 * @return int[]
919
-	 */
920
-	public function getAll() {
921
-		$query = $this->getQueryBuilder();
922
-		$query->select('fileid')
923
-			->from('filecache')
924
-			->whereStorageId();
925
-
926
-		return array_map(function ($id) {
927
-			return (int)$id;
928
-		}, $query->execute()->fetchAll(\PDO::FETCH_COLUMN));
929
-	}
930
-
931
-	/**
932
-	 * find a folder in the cache which has not been fully scanned
933
-	 *
934
-	 * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
935
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
936
-	 * likely the folder where we stopped scanning previously
937
-	 *
938
-	 * @return string|bool the path of the folder or false when no folder matched
939
-	 */
940
-	public function getIncomplete() {
941
-		$query = $this->getQueryBuilder();
942
-		$query->select('path')
943
-			->from('filecache')
944
-			->whereStorageId()
945
-			->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
946
-			->orderBy('fileid', 'DESC');
947
-
948
-		return $query->execute()->fetchColumn();
949
-	}
950
-
951
-	/**
952
-	 * get the path of a file on this storage by it's file id
953
-	 *
954
-	 * @param int $id the file id of the file or folder to search
955
-	 * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
956
-	 */
957
-	public function getPathById($id) {
958
-		$query = $this->getQueryBuilder();
959
-		$query->select('path')
960
-			->from('filecache')
961
-			->whereStorageId()
962
-			->whereFileId($id);
963
-
964
-		$path = $query->execute()->fetchColumn();
965
-		return $path === false ? null : $path;
966
-	}
967
-
968
-	/**
969
-	 * get the storage id of the storage for a file and the internal path of the file
970
-	 * unlike getPathById this does not limit the search to files on this storage and
971
-	 * instead does a global search in the cache table
972
-	 *
973
-	 * @param int $id
974
-	 * @return array first element holding the storage id, second the path
975
-	 * @deprecated use getPathById() instead
976
-	 */
977
-	public static function getById($id) {
978
-		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
979
-		$query->select('path', 'storage')
980
-			->from('filecache')
981
-			->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
982
-		if ($row = $query->execute()->fetch()) {
983
-			$numericId = $row['storage'];
984
-			$path = $row['path'];
985
-		} else {
986
-			return null;
987
-		}
988
-
989
-		if ($id = Storage::getStorageId($numericId)) {
990
-			return [$id, $path];
991
-		} else {
992
-			return null;
993
-		}
994
-	}
995
-
996
-	/**
997
-	 * normalize the given path
998
-	 *
999
-	 * @param string $path
1000
-	 * @return string
1001
-	 */
1002
-	public function normalize($path) {
1003
-		return trim(\OC_Util::normalizeUnicode($path), '/');
1004
-	}
65
+    use MoveFromCacheTrait {
66
+        MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
67
+    }
68
+
69
+    /**
70
+     * @var array partial data for the cache
71
+     */
72
+    protected $partial = [];
73
+
74
+    /**
75
+     * @var string
76
+     */
77
+    protected $storageId;
78
+
79
+    private $storage;
80
+
81
+    /**
82
+     * @var Storage $storageCache
83
+     */
84
+    protected $storageCache;
85
+
86
+    /** @var IMimeTypeLoader */
87
+    protected $mimetypeLoader;
88
+
89
+    /**
90
+     * @var IDBConnection
91
+     */
92
+    protected $connection;
93
+
94
+    protected $eventDispatcher;
95
+
96
+    /** @var QuerySearchHelper */
97
+    protected $querySearchHelper;
98
+
99
+    /**
100
+     * @param IStorage $storage
101
+     */
102
+    public function __construct(IStorage $storage) {
103
+        $this->storageId = $storage->getId();
104
+        $this->storage = $storage;
105
+        if (strlen($this->storageId) > 64) {
106
+            $this->storageId = md5($this->storageId);
107
+        }
108
+
109
+        $this->storageCache = new Storage($storage);
110
+        $this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
111
+        $this->connection = \OC::$server->getDatabaseConnection();
112
+        $this->eventDispatcher = \OC::$server->getEventDispatcher();
113
+        $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
114
+    }
115
+
116
+    private function getQueryBuilder() {
117
+        return new CacheQueryBuilder(
118
+            $this->connection,
119
+            \OC::$server->getSystemConfig(),
120
+            \OC::$server->getLogger(),
121
+            $this
122
+        );
123
+    }
124
+
125
+    /**
126
+     * Get the numeric storage id for this cache's storage
127
+     *
128
+     * @return int
129
+     */
130
+    public function getNumericStorageId() {
131
+        return $this->storageCache->getNumericId();
132
+    }
133
+
134
+    /**
135
+     * get the stored metadata of a file or folder
136
+     *
137
+     * @param string | int $file either the path of a file or folder or the file id for a file or folder
138
+     * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
139
+     */
140
+    public function get($file) {
141
+        $query = $this->getQueryBuilder();
142
+        $query->selectFileCache();
143
+
144
+        if (is_string($file) or $file == '') {
145
+            // normalize file
146
+            $file = $this->normalize($file);
147
+
148
+            $query->whereStorageId()
149
+                ->wherePath($file);
150
+        } else { //file id
151
+            $query->whereFileId($file);
152
+        }
153
+
154
+        $data = $query->execute()->fetch();
155
+
156
+        //merge partial data
157
+        if (!$data and is_string($file) and isset($this->partial[$file])) {
158
+            return $this->partial[$file];
159
+        } elseif (!$data) {
160
+            return $data;
161
+        } else {
162
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
163
+        }
164
+    }
165
+
166
+    /**
167
+     * Create a CacheEntry from database row
168
+     *
169
+     * @param array $data
170
+     * @param IMimeTypeLoader $mimetypeLoader
171
+     * @return CacheEntry
172
+     */
173
+    public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
174
+        //fix types
175
+        $data['fileid'] = (int)$data['fileid'];
176
+        $data['parent'] = (int)$data['parent'];
177
+        $data['size'] = 0 + $data['size'];
178
+        $data['mtime'] = (int)$data['mtime'];
179
+        $data['storage_mtime'] = (int)$data['storage_mtime'];
180
+        $data['encryptedVersion'] = (int)$data['encrypted'];
181
+        $data['encrypted'] = (bool)$data['encrypted'];
182
+        $data['storage_id'] = $data['storage'];
183
+        $data['storage'] = (int)$data['storage'];
184
+        $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
185
+        $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
186
+        if ($data['storage_mtime'] == 0) {
187
+            $data['storage_mtime'] = $data['mtime'];
188
+        }
189
+        $data['permissions'] = (int)$data['permissions'];
190
+        if (isset($data['creation_time'])) {
191
+            $data['creation_time'] = (int) $data['creation_time'];
192
+        }
193
+        if (isset($data['upload_time'])) {
194
+            $data['upload_time'] = (int) $data['upload_time'];
195
+        }
196
+        return new CacheEntry($data);
197
+    }
198
+
199
+    /**
200
+     * get the metadata of all files stored in $folder
201
+     *
202
+     * @param string $folder
203
+     * @return ICacheEntry[]
204
+     */
205
+    public function getFolderContents($folder) {
206
+        $fileId = $this->getId($folder);
207
+        return $this->getFolderContentsById($fileId);
208
+    }
209
+
210
+    /**
211
+     * get the metadata of all files stored in $folder
212
+     *
213
+     * @param int $fileId the file id of the folder
214
+     * @return ICacheEntry[]
215
+     */
216
+    public function getFolderContentsById($fileId) {
217
+        if ($fileId > -1) {
218
+            $query = $this->getQueryBuilder();
219
+            $query->selectFileCache()
220
+                ->whereParent($fileId)
221
+                ->orderBy('name', 'ASC');
222
+
223
+            $files = $query->execute()->fetchAll();
224
+            return array_map(function (array $data) {
225
+                return self::cacheEntryFromData($data, $this->mimetypeLoader);
226
+            }, $files);
227
+        }
228
+        return [];
229
+    }
230
+
231
+    /**
232
+     * insert or update meta data for a file or folder
233
+     *
234
+     * @param string $file
235
+     * @param array $data
236
+     *
237
+     * @return int file id
238
+     * @throws \RuntimeException
239
+     */
240
+    public function put($file, array $data) {
241
+        if (($id = $this->getId($file)) > -1) {
242
+            $this->update($id, $data);
243
+            return $id;
244
+        } else {
245
+            return $this->insert($file, $data);
246
+        }
247
+    }
248
+
249
+    /**
250
+     * insert meta data for a new file or folder
251
+     *
252
+     * @param string $file
253
+     * @param array $data
254
+     *
255
+     * @return int file id
256
+     * @throws \RuntimeException
257
+     *
258
+     * @suppress SqlInjectionChecker
259
+     */
260
+    public function insert($file, array $data) {
261
+        // normalize file
262
+        $file = $this->normalize($file);
263
+
264
+        if (isset($this->partial[$file])) { //add any saved partial data
265
+            $data = array_merge($this->partial[$file], $data);
266
+            unset($this->partial[$file]);
267
+        }
268
+
269
+        $requiredFields = ['size', 'mtime', 'mimetype'];
270
+        foreach ($requiredFields as $field) {
271
+            if (!isset($data[$field])) { //data not complete save as partial and return
272
+                $this->partial[$file] = $data;
273
+                return -1;
274
+            }
275
+        }
276
+
277
+        $data['path'] = $file;
278
+        if (!isset($data['parent'])) {
279
+            $data['parent'] = $this->getParentId($file);
280
+        }
281
+        $data['name'] = basename($file);
282
+
283
+        [$values, $extensionValues] = $this->normalizeData($data);
284
+        $values['storage'] = $this->getNumericStorageId();
285
+
286
+        try {
287
+            $builder = $this->connection->getQueryBuilder();
288
+            $builder->insert('filecache');
289
+
290
+            foreach ($values as $column => $value) {
291
+                $builder->setValue($column, $builder->createNamedParameter($value));
292
+            }
293
+
294
+            if ($builder->execute()) {
295
+                $fileId = $builder->getLastInsertId();
296
+
297
+                if (count($extensionValues)) {
298
+                    $query = $this->getQueryBuilder();
299
+                    $query->insert('filecache_extended');
300
+
301
+                    $query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
302
+                    foreach ($extensionValues as $column => $value) {
303
+                        $query->setValue($column, $query->createNamedParameter($value));
304
+                    }
305
+                    $query->execute();
306
+                }
307
+
308
+                $this->eventDispatcher->dispatch(CacheInsertEvent::class, new CacheInsertEvent($this->storage, $file, $fileId));
309
+                return $fileId;
310
+            }
311
+        } catch (UniqueConstraintViolationException $e) {
312
+            // entry exists already
313
+            if ($this->connection->inTransaction()) {
314
+                $this->connection->commit();
315
+                $this->connection->beginTransaction();
316
+            }
317
+        }
318
+
319
+        // The file was created in the mean time
320
+        if (($id = $this->getId($file)) > -1) {
321
+            $this->update($id, $data);
322
+            return $id;
323
+        } else {
324
+            throw new \RuntimeException('File entry could not be inserted but could also not be selected with getId() in order to perform an update. Please try again.');
325
+        }
326
+    }
327
+
328
+    /**
329
+     * update the metadata of an existing file or folder in the cache
330
+     *
331
+     * @param int $id the fileid of the existing file or folder
332
+     * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
333
+     */
334
+    public function update($id, array $data) {
335
+        if (isset($data['path'])) {
336
+            // normalize path
337
+            $data['path'] = $this->normalize($data['path']);
338
+        }
339
+
340
+        if (isset($data['name'])) {
341
+            // normalize path
342
+            $data['name'] = $this->normalize($data['name']);
343
+        }
344
+
345
+        [$values, $extensionValues] = $this->normalizeData($data);
346
+
347
+        if (count($values)) {
348
+            $query = $this->getQueryBuilder();
349
+
350
+            $query->update('filecache')
351
+                ->whereFileId($id)
352
+                ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
353
+                    return $query->expr()->orX(
354
+                        $query->expr()->neq($key, $query->createNamedParameter($value)),
355
+                        $query->expr()->isNull($key)
356
+                    );
357
+                }, array_keys($values), array_values($values))));
358
+
359
+            foreach ($values as $key => $value) {
360
+                $query->set($key, $query->createNamedParameter($value));
361
+            }
362
+
363
+            $query->execute();
364
+        }
365
+
366
+        if (count($extensionValues)) {
367
+            try {
368
+                $query = $this->getQueryBuilder();
369
+                $query->insert('filecache_extended');
370
+
371
+                $query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
372
+                foreach ($extensionValues as $column => $value) {
373
+                    $query->setValue($column, $query->createNamedParameter($value));
374
+                }
375
+
376
+                $query->execute();
377
+            } catch (UniqueConstraintViolationException $e) {
378
+                $query = $this->getQueryBuilder();
379
+                $query->update('filecache_extended')
380
+                    ->whereFileId($id)
381
+                    ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
382
+                        return $query->expr()->orX(
383
+                            $query->expr()->neq($key, $query->createNamedParameter($value)),
384
+                            $query->expr()->isNull($key)
385
+                        );
386
+                    }, array_keys($extensionValues), array_values($extensionValues))));
387
+
388
+                foreach ($extensionValues as $key => $value) {
389
+                    $query->set($key, $query->createNamedParameter($value));
390
+                }
391
+
392
+                $query->execute();
393
+            }
394
+        }
395
+
396
+        $path = $this->getPathById($id);
397
+        // path can still be null if the file doesn't exist
398
+        if ($path !== null) {
399
+            $this->eventDispatcher->dispatch(CacheUpdateEvent::class, new CacheUpdateEvent($this->storage, $path, $id));
400
+        }
401
+    }
402
+
403
+    /**
404
+     * extract query parts and params array from data array
405
+     *
406
+     * @param array $data
407
+     * @return array
408
+     */
409
+    protected function normalizeData(array $data): array {
410
+        $fields = [
411
+            'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
412
+            'etag', 'permissions', 'checksum', 'storage'];
413
+        $extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
414
+
415
+        $doNotCopyStorageMTime = false;
416
+        if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
417
+            // this horrific magic tells it to not copy storage_mtime to mtime
418
+            unset($data['mtime']);
419
+            $doNotCopyStorageMTime = true;
420
+        }
421
+
422
+        $params = [];
423
+        $extensionParams = [];
424
+        foreach ($data as $name => $value) {
425
+            if (array_search($name, $fields) !== false) {
426
+                if ($name === 'path') {
427
+                    $params['path_hash'] = md5($value);
428
+                } elseif ($name === 'mimetype') {
429
+                    $params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
430
+                    $value = $this->mimetypeLoader->getId($value);
431
+                } elseif ($name === 'storage_mtime') {
432
+                    if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
433
+                        $params['mtime'] = $value;
434
+                    }
435
+                } elseif ($name === 'encrypted') {
436
+                    if (isset($data['encryptedVersion'])) {
437
+                        $value = $data['encryptedVersion'];
438
+                    } else {
439
+                        // Boolean to integer conversion
440
+                        $value = $value ? 1 : 0;
441
+                    }
442
+                }
443
+                $params[$name] = $value;
444
+            }
445
+            if (array_search($name, $extensionFields) !== false) {
446
+                $extensionParams[$name] = $value;
447
+            }
448
+        }
449
+        return [$params, array_filter($extensionParams)];
450
+    }
451
+
452
+    /**
453
+     * get the file id for a file
454
+     *
455
+     * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
456
+     *
457
+     * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
458
+     *
459
+     * @param string $file
460
+     * @return int
461
+     */
462
+    public function getId($file) {
463
+        // normalize file
464
+        $file = $this->normalize($file);
465
+
466
+        $query = $this->getQueryBuilder();
467
+        $query->select('fileid')
468
+            ->from('filecache')
469
+            ->whereStorageId()
470
+            ->wherePath($file);
471
+
472
+        $id = $query->execute()->fetchColumn();
473
+        return $id === false ? -1 : (int)$id;
474
+    }
475
+
476
+    /**
477
+     * get the id of the parent folder of a file
478
+     *
479
+     * @param string $file
480
+     * @return int
481
+     */
482
+    public function getParentId($file) {
483
+        if ($file === '') {
484
+            return -1;
485
+        } else {
486
+            $parent = $this->getParentPath($file);
487
+            return (int)$this->getId($parent);
488
+        }
489
+    }
490
+
491
+    private function getParentPath($path) {
492
+        $parent = dirname($path);
493
+        if ($parent === '.') {
494
+            $parent = '';
495
+        }
496
+        return $parent;
497
+    }
498
+
499
+    /**
500
+     * check if a file is available in the cache
501
+     *
502
+     * @param string $file
503
+     * @return bool
504
+     */
505
+    public function inCache($file) {
506
+        return $this->getId($file) != -1;
507
+    }
508
+
509
+    /**
510
+     * remove a file or folder from the cache
511
+     *
512
+     * when removing a folder from the cache all files and folders inside the folder will be removed as well
513
+     *
514
+     * @param string $file
515
+     */
516
+    public function remove($file) {
517
+        $entry = $this->get($file);
518
+
519
+        if ($entry) {
520
+            $query = $this->getQueryBuilder();
521
+            $query->delete('filecache')
522
+                ->whereFileId($entry->getId());
523
+            $query->execute();
524
+
525
+            $query = $this->getQueryBuilder();
526
+            $query->delete('filecache_extended')
527
+                ->whereFileId($entry->getId());
528
+            $query->execute();
529
+
530
+            if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
531
+                $this->removeChildren($entry);
532
+            }
533
+        }
534
+    }
535
+
536
+    /**
537
+     * Get all sub folders of a folder
538
+     *
539
+     * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for
540
+     * @return ICacheEntry[] the cache entries for the subfolders
541
+     */
542
+    private function getSubFolders(ICacheEntry $entry) {
543
+        $children = $this->getFolderContentsById($entry->getId());
544
+        return array_filter($children, function ($child) {
545
+            return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
546
+        });
547
+    }
548
+
549
+    /**
550
+     * Recursively remove all children of a folder
551
+     *
552
+     * @param ICacheEntry $entry the cache entry of the folder to remove the children of
553
+     * @throws \OC\DatabaseException
554
+     */
555
+    private function removeChildren(ICacheEntry $entry) {
556
+        $parentIds = [$entry->getId()];
557
+        $queue = [$entry->getId()];
558
+
559
+        // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
560
+        // and collecting all folder ids to later use to delete the filecache entries
561
+        while ($entryId = array_pop($queue)) {
562
+            $children = $this->getFolderContentsById($entryId);
563
+            $childIds = array_map(function (ICacheEntry $cacheEntry) {
564
+                return $cacheEntry->getId();
565
+            }, $children);
566
+
567
+            $query = $this->getQueryBuilder();
568
+            $query->delete('filecache_extended')
569
+                ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY)));
570
+            $query->execute();
571
+
572
+            /** @var ICacheEntry[] $childFolders */
573
+            $childFolders = array_filter($children, function ($child) {
574
+                return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
575
+            });
576
+            foreach ($childFolders as $folder) {
577
+                $parentIds[] = $folder->getId();
578
+                $queue[] = $folder->getId();
579
+            }
580
+        }
581
+
582
+        $query = $this->getQueryBuilder();
583
+        $query->delete('filecache')
584
+            ->whereParentIn($parentIds);
585
+        $query->execute();
586
+    }
587
+
588
+    /**
589
+     * Move a file or folder in the cache
590
+     *
591
+     * @param string $source
592
+     * @param string $target
593
+     */
594
+    public function move($source, $target) {
595
+        $this->moveFromCache($this, $source, $target);
596
+    }
597
+
598
+    /**
599
+     * Get the storage id and path needed for a move
600
+     *
601
+     * @param string $path
602
+     * @return array [$storageId, $internalPath]
603
+     */
604
+    protected function getMoveInfo($path) {
605
+        return [$this->getNumericStorageId(), $path];
606
+    }
607
+
608
+    /**
609
+     * Move a file or folder in the cache
610
+     *
611
+     * @param \OCP\Files\Cache\ICache $sourceCache
612
+     * @param string $sourcePath
613
+     * @param string $targetPath
614
+     * @throws \OC\DatabaseException
615
+     * @throws \Exception if the given storages have an invalid id
616
+     * @suppress SqlInjectionChecker
617
+     */
618
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
619
+        if ($sourceCache instanceof Cache) {
620
+            // normalize source and target
621
+            $sourcePath = $this->normalize($sourcePath);
622
+            $targetPath = $this->normalize($targetPath);
623
+
624
+            $sourceData = $sourceCache->get($sourcePath);
625
+            $sourceId = $sourceData['fileid'];
626
+            $newParentId = $this->getParentId($targetPath);
627
+
628
+            [$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
629
+            [$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
630
+
631
+            if (is_null($sourceStorageId) || $sourceStorageId === false) {
632
+                throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
633
+            }
634
+            if (is_null($targetStorageId) || $targetStorageId === false) {
635
+                throw new \Exception('Invalid target storage id: ' . $targetStorageId);
636
+            }
637
+
638
+            $this->connection->beginTransaction();
639
+            if ($sourceData['mimetype'] === 'httpd/unix-directory') {
640
+                //update all child entries
641
+                $sourceLength = mb_strlen($sourcePath);
642
+                $query = $this->connection->getQueryBuilder();
643
+
644
+                $fun = $query->func();
645
+                $newPathFunction = $fun->concat(
646
+                    $query->createNamedParameter($targetPath),
647
+                    $fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
648
+                );
649
+                $query->update('filecache')
650
+                    ->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
651
+                    ->set('path_hash', $fun->md5($newPathFunction))
652
+                    ->set('path', $newPathFunction)
653
+                    ->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
654
+                    ->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
655
+
656
+                try {
657
+                    $query->execute();
658
+                } catch (\OC\DatabaseException $e) {
659
+                    $this->connection->rollBack();
660
+                    throw $e;
661
+                }
662
+            }
663
+
664
+            $query = $this->getQueryBuilder();
665
+            $query->update('filecache')
666
+                ->set('storage', $query->createNamedParameter($targetStorageId))
667
+                ->set('path', $query->createNamedParameter($targetPath))
668
+                ->set('path_hash', $query->createNamedParameter(md5($targetPath)))
669
+                ->set('name', $query->createNamedParameter(basename($targetPath)))
670
+                ->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
671
+                ->whereFileId($sourceId);
672
+            $query->execute();
673
+
674
+            $this->connection->commit();
675
+        } else {
676
+            $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
677
+        }
678
+    }
679
+
680
+    /**
681
+     * remove all entries for files that are stored on the storage from the cache
682
+     */
683
+    public function clear() {
684
+        $query = $this->getQueryBuilder();
685
+        $query->delete('filecache')
686
+            ->whereStorageId();
687
+        $query->execute();
688
+
689
+        $query = $this->connection->getQueryBuilder();
690
+        $query->delete('storages')
691
+            ->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
692
+        $query->execute();
693
+    }
694
+
695
+    /**
696
+     * Get the scan status of a file
697
+     *
698
+     * - Cache::NOT_FOUND: File is not in the cache
699
+     * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
700
+     * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
701
+     * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
702
+     *
703
+     * @param string $file
704
+     *
705
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
706
+     */
707
+    public function getStatus($file) {
708
+        // normalize file
709
+        $file = $this->normalize($file);
710
+
711
+        $query = $this->getQueryBuilder();
712
+        $query->select('size')
713
+            ->from('filecache')
714
+            ->whereStorageId()
715
+            ->wherePath($file);
716
+        $size = $query->execute()->fetchColumn();
717
+        if ($size !== false) {
718
+            if ((int)$size === -1) {
719
+                return self::SHALLOW;
720
+            } else {
721
+                return self::COMPLETE;
722
+            }
723
+        } else {
724
+            if (isset($this->partial[$file])) {
725
+                return self::PARTIAL;
726
+            } else {
727
+                return self::NOT_FOUND;
728
+            }
729
+        }
730
+    }
731
+
732
+    /**
733
+     * search for files matching $pattern
734
+     *
735
+     * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
736
+     * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
737
+     */
738
+    public function search($pattern) {
739
+        // normalize pattern
740
+        $pattern = $this->normalize($pattern);
741
+
742
+        if ($pattern === '%%') {
743
+            return [];
744
+        }
745
+
746
+        $query = $this->getQueryBuilder();
747
+        $query->selectFileCache()
748
+            ->whereStorageId()
749
+            ->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern)));
750
+
751
+        return array_map(function (array $data) {
752
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
753
+        }, $query->execute()->fetchAll());
754
+    }
755
+
756
+    /**
757
+     * @param Statement $result
758
+     * @return CacheEntry[]
759
+     */
760
+    private function searchResultToCacheEntries(Statement $result) {
761
+        $files = $result->fetchAll();
762
+
763
+        return array_map(function (array $data) {
764
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
765
+        }, $files);
766
+    }
767
+
768
+    /**
769
+     * search for files by mimetype
770
+     *
771
+     * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
772
+     *        where it will search for all mimetypes in the group ('image/*')
773
+     * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
774
+     */
775
+    public function searchByMime($mimetype) {
776
+        $mimeId = $this->mimetypeLoader->getId($mimetype);
777
+
778
+        $query = $this->getQueryBuilder();
779
+        $query->selectFileCache()
780
+            ->whereStorageId();
781
+
782
+        if (strpos($mimetype, '/')) {
783
+            $query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
784
+        } else {
785
+            $query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
786
+        }
787
+
788
+        return array_map(function (array $data) {
789
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
790
+        }, $query->execute()->fetchAll());
791
+    }
792
+
793
+    public function searchQuery(ISearchQuery $searchQuery) {
794
+        $builder = $this->getQueryBuilder();
795
+
796
+        $query = $builder->selectFileCache('file');
797
+
798
+        $query->whereStorageId();
799
+
800
+        if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
801
+            $query
802
+                ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
803
+                ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
804
+                    $builder->expr()->eq('tagmap.type', 'tag.type'),
805
+                    $builder->expr()->eq('tagmap.categoryid', 'tag.id')
806
+                ))
807
+                ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
808
+                ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
809
+        }
810
+
811
+        $searchExpr = $this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation());
812
+        if ($searchExpr) {
813
+            $query->andWhere($searchExpr);
814
+        }
815
+
816
+        if ($searchQuery->limitToHome() && ($this instanceof HomeCache)) {
817
+            $query->andWhere($builder->expr()->like('path', $query->expr()->literal('files/%')));
818
+        }
819
+
820
+        $this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
821
+
822
+        if ($searchQuery->getLimit()) {
823
+            $query->setMaxResults($searchQuery->getLimit());
824
+        }
825
+        if ($searchQuery->getOffset()) {
826
+            $query->setFirstResult($searchQuery->getOffset());
827
+        }
828
+
829
+        $result = $query->execute();
830
+        return $this->searchResultToCacheEntries($result);
831
+    }
832
+
833
+    /**
834
+     * Re-calculate the folder size and the size of all parent folders
835
+     *
836
+     * @param string|boolean $path
837
+     * @param array $data (optional) meta data of the folder
838
+     */
839
+    public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
840
+        $this->calculateFolderSize($path, $data);
841
+        if ($path !== '') {
842
+            $parent = dirname($path);
843
+            if ($parent === '.' or $parent === '/') {
844
+                $parent = '';
845
+            }
846
+            if ($isBackgroundScan) {
847
+                $parentData = $this->get($parent);
848
+                if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
849
+                    $this->correctFolderSize($parent, $parentData, $isBackgroundScan);
850
+                }
851
+            } else {
852
+                $this->correctFolderSize($parent);
853
+            }
854
+        }
855
+    }
856
+
857
+    /**
858
+     * get the incomplete count that shares parent $folder
859
+     *
860
+     * @param int $fileId the file id of the folder
861
+     * @return int
862
+     */
863
+    public function getIncompleteChildrenCount($fileId) {
864
+        if ($fileId > -1) {
865
+            $query = $this->getQueryBuilder();
866
+            $query->select($query->func()->count())
867
+                ->from('filecache')
868
+                ->whereParent($fileId)
869
+                ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
870
+
871
+            return (int)$query->execute()->fetchColumn();
872
+        }
873
+        return -1;
874
+    }
875
+
876
+    /**
877
+     * calculate the size of a folder and set it in the cache
878
+     *
879
+     * @param string $path
880
+     * @param array $entry (optional) meta data of the folder
881
+     * @return int
882
+     */
883
+    public function calculateFolderSize($path, $entry = null) {
884
+        $totalSize = 0;
885
+        if (is_null($entry) or !isset($entry['fileid'])) {
886
+            $entry = $this->get($path);
887
+        }
888
+        if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
889
+            $id = $entry['fileid'];
890
+
891
+            $query = $this->getQueryBuilder();
892
+            $query->selectAlias($query->func()->sum('size'), 'f1')
893
+                ->selectAlias($query->func()->min('size'), 'f2')
894
+                ->from('filecache')
895
+                ->whereStorageId()
896
+                ->whereParent($id);
897
+
898
+            if ($row = $query->execute()->fetch()) {
899
+                [$sum, $min] = array_values($row);
900
+                $sum = 0 + $sum;
901
+                $min = 0 + $min;
902
+                if ($min === -1) {
903
+                    $totalSize = $min;
904
+                } else {
905
+                    $totalSize = $sum;
906
+                }
907
+                if ($entry['size'] !== $totalSize) {
908
+                    $this->update($id, ['size' => $totalSize]);
909
+                }
910
+            }
911
+        }
912
+        return $totalSize;
913
+    }
914
+
915
+    /**
916
+     * get all file ids on the files on the storage
917
+     *
918
+     * @return int[]
919
+     */
920
+    public function getAll() {
921
+        $query = $this->getQueryBuilder();
922
+        $query->select('fileid')
923
+            ->from('filecache')
924
+            ->whereStorageId();
925
+
926
+        return array_map(function ($id) {
927
+            return (int)$id;
928
+        }, $query->execute()->fetchAll(\PDO::FETCH_COLUMN));
929
+    }
930
+
931
+    /**
932
+     * find a folder in the cache which has not been fully scanned
933
+     *
934
+     * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
935
+     * use the one with the highest id gives the best result with the background scanner, since that is most
936
+     * likely the folder where we stopped scanning previously
937
+     *
938
+     * @return string|bool the path of the folder or false when no folder matched
939
+     */
940
+    public function getIncomplete() {
941
+        $query = $this->getQueryBuilder();
942
+        $query->select('path')
943
+            ->from('filecache')
944
+            ->whereStorageId()
945
+            ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
946
+            ->orderBy('fileid', 'DESC');
947
+
948
+        return $query->execute()->fetchColumn();
949
+    }
950
+
951
+    /**
952
+     * get the path of a file on this storage by it's file id
953
+     *
954
+     * @param int $id the file id of the file or folder to search
955
+     * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
956
+     */
957
+    public function getPathById($id) {
958
+        $query = $this->getQueryBuilder();
959
+        $query->select('path')
960
+            ->from('filecache')
961
+            ->whereStorageId()
962
+            ->whereFileId($id);
963
+
964
+        $path = $query->execute()->fetchColumn();
965
+        return $path === false ? null : $path;
966
+    }
967
+
968
+    /**
969
+     * get the storage id of the storage for a file and the internal path of the file
970
+     * unlike getPathById this does not limit the search to files on this storage and
971
+     * instead does a global search in the cache table
972
+     *
973
+     * @param int $id
974
+     * @return array first element holding the storage id, second the path
975
+     * @deprecated use getPathById() instead
976
+     */
977
+    public static function getById($id) {
978
+        $query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
979
+        $query->select('path', 'storage')
980
+            ->from('filecache')
981
+            ->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
982
+        if ($row = $query->execute()->fetch()) {
983
+            $numericId = $row['storage'];
984
+            $path = $row['path'];
985
+        } else {
986
+            return null;
987
+        }
988
+
989
+        if ($id = Storage::getStorageId($numericId)) {
990
+            return [$id, $path];
991
+        } else {
992
+            return null;
993
+        }
994
+    }
995
+
996
+    /**
997
+     * normalize the given path
998
+     *
999
+     * @param string $path
1000
+     * @return string
1001
+     */
1002
+    public function normalize($path) {
1003
+        return trim(\OC_Util::normalizeUnicode($path), '/');
1004
+    }
1005 1005
 }
Please login to merge, or discard this patch.
Spacing   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -172,21 +172,21 @@  discard block
 block discarded – undo
172 172
 	 */
173 173
 	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
174 174
 		//fix types
175
-		$data['fileid'] = (int)$data['fileid'];
176
-		$data['parent'] = (int)$data['parent'];
175
+		$data['fileid'] = (int) $data['fileid'];
176
+		$data['parent'] = (int) $data['parent'];
177 177
 		$data['size'] = 0 + $data['size'];
178
-		$data['mtime'] = (int)$data['mtime'];
179
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
180
-		$data['encryptedVersion'] = (int)$data['encrypted'];
181
-		$data['encrypted'] = (bool)$data['encrypted'];
178
+		$data['mtime'] = (int) $data['mtime'];
179
+		$data['storage_mtime'] = (int) $data['storage_mtime'];
180
+		$data['encryptedVersion'] = (int) $data['encrypted'];
181
+		$data['encrypted'] = (bool) $data['encrypted'];
182 182
 		$data['storage_id'] = $data['storage'];
183
-		$data['storage'] = (int)$data['storage'];
183
+		$data['storage'] = (int) $data['storage'];
184 184
 		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
185 185
 		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
186 186
 		if ($data['storage_mtime'] == 0) {
187 187
 			$data['storage_mtime'] = $data['mtime'];
188 188
 		}
189
-		$data['permissions'] = (int)$data['permissions'];
189
+		$data['permissions'] = (int) $data['permissions'];
190 190
 		if (isset($data['creation_time'])) {
191 191
 			$data['creation_time'] = (int) $data['creation_time'];
192 192
 		}
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
 				->orderBy('name', 'ASC');
222 222
 
223 223
 			$files = $query->execute()->fetchAll();
224
-			return array_map(function (array $data) {
224
+			return array_map(function(array $data) {
225 225
 				return self::cacheEntryFromData($data, $this->mimetypeLoader);
226 226
 			}, $files);
227 227
 		}
@@ -349,7 +349,7 @@  discard block
 block discarded – undo
349 349
 
350 350
 			$query->update('filecache')
351 351
 				->whereFileId($id)
352
-				->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
352
+				->andWhere($query->expr()->orX(...array_map(function($key, $value) use ($query) {
353 353
 					return $query->expr()->orX(
354 354
 						$query->expr()->neq($key, $query->createNamedParameter($value)),
355 355
 						$query->expr()->isNull($key)
@@ -378,7 +378,7 @@  discard block
 block discarded – undo
378 378
 				$query = $this->getQueryBuilder();
379 379
 				$query->update('filecache_extended')
380 380
 					->whereFileId($id)
381
-					->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
381
+					->andWhere($query->expr()->orX(...array_map(function($key, $value) use ($query) {
382 382
 						return $query->expr()->orX(
383 383
 							$query->expr()->neq($key, $query->createNamedParameter($value)),
384 384
 							$query->expr()->isNull($key)
@@ -470,7 +470,7 @@  discard block
 block discarded – undo
470 470
 			->wherePath($file);
471 471
 
472 472
 		$id = $query->execute()->fetchColumn();
473
-		return $id === false ? -1 : (int)$id;
473
+		return $id === false ? -1 : (int) $id;
474 474
 	}
475 475
 
476 476
 	/**
@@ -484,7 +484,7 @@  discard block
 block discarded – undo
484 484
 			return -1;
485 485
 		} else {
486 486
 			$parent = $this->getParentPath($file);
487
-			return (int)$this->getId($parent);
487
+			return (int) $this->getId($parent);
488 488
 		}
489 489
 	}
490 490
 
@@ -541,7 +541,7 @@  discard block
 block discarded – undo
541 541
 	 */
542 542
 	private function getSubFolders(ICacheEntry $entry) {
543 543
 		$children = $this->getFolderContentsById($entry->getId());
544
-		return array_filter($children, function ($child) {
544
+		return array_filter($children, function($child) {
545 545
 			return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
546 546
 		});
547 547
 	}
@@ -560,7 +560,7 @@  discard block
 block discarded – undo
560 560
 		// and collecting all folder ids to later use to delete the filecache entries
561 561
 		while ($entryId = array_pop($queue)) {
562 562
 			$children = $this->getFolderContentsById($entryId);
563
-			$childIds = array_map(function (ICacheEntry $cacheEntry) {
563
+			$childIds = array_map(function(ICacheEntry $cacheEntry) {
564 564
 				return $cacheEntry->getId();
565 565
 			}, $children);
566 566
 
@@ -570,7 +570,7 @@  discard block
 block discarded – undo
570 570
 			$query->execute();
571 571
 
572 572
 			/** @var ICacheEntry[] $childFolders */
573
-			$childFolders = array_filter($children, function ($child) {
573
+			$childFolders = array_filter($children, function($child) {
574 574
 				return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
575 575
 			});
576 576
 			foreach ($childFolders as $folder) {
@@ -629,10 +629,10 @@  discard block
 block discarded – undo
629 629
 			[$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
630 630
 
631 631
 			if (is_null($sourceStorageId) || $sourceStorageId === false) {
632
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
632
+				throw new \Exception('Invalid source storage id: '.$sourceStorageId);
633 633
 			}
634 634
 			if (is_null($targetStorageId) || $targetStorageId === false) {
635
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
635
+				throw new \Exception('Invalid target storage id: '.$targetStorageId);
636 636
 			}
637 637
 
638 638
 			$this->connection->beginTransaction();
@@ -651,7 +651,7 @@  discard block
 block discarded – undo
651 651
 					->set('path_hash', $fun->md5($newPathFunction))
652 652
 					->set('path', $newPathFunction)
653 653
 					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
654
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
654
+					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath).'/%')));
655 655
 
656 656
 				try {
657 657
 					$query->execute();
@@ -715,7 +715,7 @@  discard block
 block discarded – undo
715 715
 			->wherePath($file);
716 716
 		$size = $query->execute()->fetchColumn();
717 717
 		if ($size !== false) {
718
-			if ((int)$size === -1) {
718
+			if ((int) $size === -1) {
719 719
 				return self::SHALLOW;
720 720
 			} else {
721 721
 				return self::COMPLETE;
@@ -748,7 +748,7 @@  discard block
 block discarded – undo
748 748
 			->whereStorageId()
749 749
 			->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern)));
750 750
 
751
-		return array_map(function (array $data) {
751
+		return array_map(function(array $data) {
752 752
 			return self::cacheEntryFromData($data, $this->mimetypeLoader);
753 753
 		}, $query->execute()->fetchAll());
754 754
 	}
@@ -760,7 +760,7 @@  discard block
 block discarded – undo
760 760
 	private function searchResultToCacheEntries(Statement $result) {
761 761
 		$files = $result->fetchAll();
762 762
 
763
-		return array_map(function (array $data) {
763
+		return array_map(function(array $data) {
764 764
 			return self::cacheEntryFromData($data, $this->mimetypeLoader);
765 765
 		}, $files);
766 766
 	}
@@ -785,7 +785,7 @@  discard block
 block discarded – undo
785 785
 			$query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
786 786
 		}
787 787
 
788
-		return array_map(function (array $data) {
788
+		return array_map(function(array $data) {
789 789
 			return self::cacheEntryFromData($data, $this->mimetypeLoader);
790 790
 		}, $query->execute()->fetchAll());
791 791
 	}
@@ -868,7 +868,7 @@  discard block
 block discarded – undo
868 868
 				->whereParent($fileId)
869 869
 				->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
870 870
 
871
-			return (int)$query->execute()->fetchColumn();
871
+			return (int) $query->execute()->fetchColumn();
872 872
 		}
873 873
 		return -1;
874 874
 	}
@@ -923,8 +923,8 @@  discard block
 block discarded – undo
923 923
 			->from('filecache')
924 924
 			->whereStorageId();
925 925
 
926
-		return array_map(function ($id) {
927
-			return (int)$id;
926
+		return array_map(function($id) {
927
+			return (int) $id;
928 928
 		}, $query->execute()->fetchAll(\PDO::FETCH_COLUMN));
929 929
 	}
930 930
 
Please login to merge, or discard this patch.
lib/private/Files/Cache/CacheQueryBuilder.php 1 patch
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -36,75 +36,75 @@
 block discarded – undo
36 36
  * Query builder with commonly used helpers for filecache queries
37 37
  */
38 38
 class CacheQueryBuilder extends QueryBuilder {
39
-	private $cache;
40
-	private $alias = null;
39
+    private $cache;
40
+    private $alias = null;
41 41
 
42
-	public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger, Cache $cache) {
43
-		parent::__construct($connection, $systemConfig, $logger);
42
+    public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger, Cache $cache) {
43
+        parent::__construct($connection, $systemConfig, $logger);
44 44
 
45
-		$this->cache = $cache;
46
-	}
45
+        $this->cache = $cache;
46
+    }
47 47
 
48
-	public function selectFileCache(string $alias = null) {
49
-		$name = $alias ? $alias : 'filecache';
50
-		$this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", 'name', 'mimetype', 'mimepart', 'size', 'mtime',
51
-			'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time')
52
-			->from('filecache', $name)
53
-			->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
48
+    public function selectFileCache(string $alias = null) {
49
+        $name = $alias ? $alias : 'filecache';
50
+        $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", 'name', 'mimetype', 'mimepart', 'size', 'mtime',
51
+            'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time')
52
+            ->from('filecache', $name)
53
+            ->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
54 54
 
55
-		$this->alias = $name;
55
+        $this->alias = $name;
56 56
 
57
-		return $this;
58
-	}
57
+        return $this;
58
+    }
59 59
 
60
-	public function whereStorageId() {
61
-		$this->andWhere($this->expr()->eq('storage', $this->createNamedParameter($this->cache->getNumericStorageId(), IQueryBuilder::PARAM_INT)));
60
+    public function whereStorageId() {
61
+        $this->andWhere($this->expr()->eq('storage', $this->createNamedParameter($this->cache->getNumericStorageId(), IQueryBuilder::PARAM_INT)));
62 62
 
63
-		return $this;
64
-	}
63
+        return $this;
64
+    }
65 65
 
66
-	public function whereFileId(int $fileId) {
67
-		$alias = $this->alias;
68
-		if ($alias) {
69
-			$alias .= '.';
70
-		} else {
71
-			$alias = '';
72
-		}
66
+    public function whereFileId(int $fileId) {
67
+        $alias = $this->alias;
68
+        if ($alias) {
69
+            $alias .= '.';
70
+        } else {
71
+            $alias = '';
72
+        }
73 73
 
74
-		$this->andWhere($this->expr()->eq("{$alias}fileid", $this->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
74
+        $this->andWhere($this->expr()->eq("{$alias}fileid", $this->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
75 75
 
76
-		return $this;
77
-	}
76
+        return $this;
77
+    }
78 78
 
79
-	public function wherePath(string $path) {
80
-		$this->andWhere($this->expr()->eq('path_hash', $this->createNamedParameter(md5($path))));
79
+    public function wherePath(string $path) {
80
+        $this->andWhere($this->expr()->eq('path_hash', $this->createNamedParameter(md5($path))));
81 81
 
82
-		return $this;
83
-	}
82
+        return $this;
83
+    }
84 84
 
85
-	public function whereParent(int $parent) {
86
-		$alias = $this->alias;
87
-		if ($alias) {
88
-			$alias .= '.';
89
-		} else {
90
-			$alias = '';
91
-		}
85
+    public function whereParent(int $parent) {
86
+        $alias = $this->alias;
87
+        if ($alias) {
88
+            $alias .= '.';
89
+        } else {
90
+            $alias = '';
91
+        }
92 92
 
93
-		$this->andWhere($this->expr()->eq("{$alias}parent", $this->createNamedParameter($parent, IQueryBuilder::PARAM_INT)));
93
+        $this->andWhere($this->expr()->eq("{$alias}parent", $this->createNamedParameter($parent, IQueryBuilder::PARAM_INT)));
94 94
 
95
-		return $this;
96
-	}
95
+        return $this;
96
+    }
97 97
 
98
-	public function whereParentIn(array $parents) {
99
-		$alias = $this->alias;
100
-		if ($alias) {
101
-			$alias .= '.';
102
-		} else {
103
-			$alias = '';
104
-		}
98
+    public function whereParentIn(array $parents) {
99
+        $alias = $this->alias;
100
+        if ($alias) {
101
+            $alias .= '.';
102
+        } else {
103
+            $alias = '';
104
+        }
105 105
 
106
-		$this->andWhere($this->expr()->in("{$alias}parent", $this->createNamedParameter($parents, IQueryBuilder::PARAM_INT_ARRAY)));
106
+        $this->andWhere($this->expr()->in("{$alias}parent", $this->createNamedParameter($parents, IQueryBuilder::PARAM_INT_ARRAY)));
107 107
 
108
-		return $this;
109
-	}
108
+        return $this;
109
+    }
110 110
 }
Please login to merge, or discard this patch.