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