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