store filecache extension fields

Signed-off-by: Robin Appelman <robin@icewind.nl>
pull/17765/head
Robin Appelman 6 years ago
parent d3b6dbc0bc
commit 842da3f183
No known key found for this signature in database
GPG Key ID: 42B69D8A64526EFB
  1. 2
      core/Migrations/Version17000Date20190514105811.php
  2. 94
      lib/private/Files/Cache/Cache.php
  3. 12
      lib/private/Files/Cache/CacheEntry.php
  4. 31
      lib/private/Files/Cache/CacheQueryBuilder.php
  5. 24
      lib/public/Files/Cache/ICacheEntry.php
  6. 61
      tests/lib/Files/Cache/CacheTest.php

@ -44,7 +44,7 @@ class Version17000Date20190514105811 extends SimpleMigrationStep {
$schema = $schemaClosure();
if(!$schema->hasTable('filecache_extended')) {
$table = $schema->createTable('filecache_extended');
$table->addColumn('fileid', Type::INTEGER, [
$table->addColumn('fileid', Type::BIGINT, [
'notnull' => true,
'length' => 4,
'unsigned' => true,

@ -187,6 +187,12 @@ class Cache implements ICache {
$data['storage_mtime'] = $data['mtime'];
}
$data['permissions'] = (int)$data['permissions'];
if (isset($data['creation_time'])) {
$data['creation_time'] = (int) $data['creation_time'];
}
if (isset($data['upload_time'])) {
$data['upload_time'] = (int) $data['upload_time'];
}
return new CacheEntry($data);
}
@ -272,7 +278,7 @@ class Cache implements ICache {
$data['parent'] = $this->getParentId($file);
$data['name'] = basename($file);
$values = $this->normalizeData($data);
[$values, $extensionValues] = $this->normalizeData($data);
$values['storage'] = $this->getNumericStorageId();
try {
@ -284,7 +290,17 @@ class Cache implements ICache {
}
if ($builder->execute()) {
$fileId = (int)$this->connection->lastInsertId('*PREFIX*filecache');
$fileId = $builder->getLastInsertId();
$query = $this->getQueryBuilder();
$query->insert('filecache_extended');
$query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
foreach ($extensionValues as $column => $value) {
$query->setValue($column, $query->createNamedParameter($value));
}
$query->execute();
$this->eventDispatcher->dispatch(CacheInsertEvent::class, new CacheInsertEvent($this->storage, $file, $fileId));
return $fileId;
}
@ -319,24 +335,44 @@ class Cache implements ICache {
$data['name'] = $this->normalize($data['name']);
}
$values = $this->normalizeData($data);
[$values, $extensionValues] = $this->normalizeData($data);
$query = $this->getQueryBuilder();
if (count($values)) {
$query = $this->getQueryBuilder();
$query->update('filecache')
->whereFileId($id)
->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
return $query->expr()->orX(
$query->expr()->neq($key, $query->createNamedParameter($value)),
$query->expr()->isNull($key)
);
}, array_keys($values), array_values($values))));
$query->update('filecache')
->whereFileId($id)
->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
return $query->expr()->orX(
$query->expr()->neq($key, $query->createNamedParameter($value)),
$query->expr()->isNull($key)
);
}, array_keys($values), array_values($values))));
foreach ($values as $key => $value) {
$query->set($key, $query->createNamedParameter($value));
}
foreach ($values as $key => $value) {
$query->set($key, $query->createNamedParameter($value));
$query->execute();
}
$query->execute();
if (count($extensionValues)) {
$query = $this->getQueryBuilder();
$query->update('filecache_extended')
->whereFileId($id)
->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
return $query->expr()->orX(
$query->expr()->neq($key, $query->createNamedParameter($value)),
$query->expr()->isNull($key)
);
}, array_keys($extensionValues), array_values($extensionValues))));
foreach ($extensionValues as $key => $value) {
$query->set($key, $query->createNamedParameter($value));
}
$query->execute();
}
$path = $this->getPathById($id);
// path can still be null if the file doesn't exist
@ -355,6 +391,7 @@ class Cache implements ICache {
$fields = [
'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
'etag', 'permissions', 'checksum', 'storage'];
$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
$doNotCopyStorageMTime = false;
if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
@ -364,6 +401,7 @@ class Cache implements ICache {
}
$params = [];
$extensionParams = [];
foreach ($data as $name => $value) {
if (array_search($name, $fields) !== false) {
if ($name === 'path') {
@ -385,8 +423,11 @@ class Cache implements ICache {
}
$params[$name] = $value;
}
if (array_search($name, $extensionFields) !== false) {
$extensionParams[$name] = $value;
}
}
return $params;
return [$params, $extensionParams];
}
/**
@ -461,6 +502,12 @@ class Cache implements ICache {
$query->delete('filecache')
->whereFileId($entry->getId());
$query->execute();
$query = $this->getQueryBuilder();
$query->delete('filecache_extended')
->whereFileId($entry->getId());
$query->execute();
if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
$this->removeChildren($entry);
}
@ -496,6 +543,11 @@ class Cache implements ICache {
$query->delete('filecache')
->whereParent($entry->getId());
$query->execute();
$query = $this->getQueryBuilder();
$query->delete('filecache_extended')
->whereParent($entry->getId());
$query->execute();
}
/**
@ -574,15 +626,16 @@ class Cache implements ICache {
}
}
$query = $this->connection->getQueryBuilder();
$query = $this->getQueryBuilder();
$query->update('filecache')
->set('storage', $query->createNamedParameter($targetStorageId))
->set('path', $query->createNamedParameter($targetPath))
->set('path_hash', $query->createNamedParameter(md5($targetPath)))
->set('name', $query->createNamedParameter(basename($targetPath)))
->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('fileid', $query->createNamedParameter($sourceId)));
->whereFileId($sourceId);
$query->execute();
$this->connection->commit();
} else {
$this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
@ -595,7 +648,7 @@ class Cache implements ICache {
public function clear() {
$query = $this->getQueryBuilder();
$query->delete('filecache')
->whereStorageId();;
->whereStorageId();
$query->execute();
$query = $this->connection->getQueryBuilder();
@ -705,8 +758,7 @@ class Cache implements ICache {
public function searchQuery(ISearchQuery $searchQuery) {
$builder = $this->getQueryBuilder();
$query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum'])
->from('filecache', 'file');
$query = $builder->selectFileCache('file');
$query->whereStorageId();

@ -109,6 +109,18 @@ class CacheEntry implements ICacheEntry, \ArrayAccess {
return isset($this->data['encrypted']) && $this->data['encrypted'];
}
public function getMetadataEtag(): ?string {
return $this->data['metadata_etag'];
}
public function getCreationTime(): ?int {
return $this->data['creation_time'];
}
public function getUploadTime(): ?int {
return $this->data['upload_time'];
}
public function getData() {
return $this->data;
}

@ -32,6 +32,7 @@ use OCP\ILogger;
*/
class CacheQueryBuilder extends QueryBuilder {
private $cache;
private $alias = null;
public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger, Cache $cache) {
parent::__construct($connection, $systemConfig, $logger);
@ -39,10 +40,14 @@ class CacheQueryBuilder extends QueryBuilder {
$this->cache = $cache;
}
public function selectFileCache() {
$this->select('fileid', 'storage', 'path', 'path_hash', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime',
'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum')
->from('filecache');
public function selectFileCache(string $alias = null) {
$name = $alias ? $alias : 'filecache';
$this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", 'name', 'mimetype', 'mimepart', 'size', 'mtime',
'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time')
->from('filecache', $name)
->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
$this->alias = $name;
return $this;
}
@ -54,7 +59,14 @@ class CacheQueryBuilder extends QueryBuilder {
}
public function whereFileId(int $fileId) {
$this->andWhere($this->expr()->eq('fileid', $this->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$alias = $this->alias;
if ($alias) {
$alias .= '.';
} else {
$alias = '';
}
$this->andWhere($this->expr()->eq("{$alias}fileid", $this->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
return $this;
}
@ -66,7 +78,14 @@ class CacheQueryBuilder extends QueryBuilder {
}
public function whereParent(int $parent) {
$this->andWhere($this->expr()->eq('parent', $this->createNamedParameter($parent, IQueryBuilder::PARAM_INT)));
$alias = $this->alias;
if ($alias) {
$alias .= '.';
} else {
$alias = '';
}
$this->andWhere($this->expr()->eq("{$alias}parent", $this->createNamedParameter($parent, IQueryBuilder::PARAM_INT)));
return $this;
}

@ -132,4 +132,28 @@ interface ICacheEntry {
* @since 9.0.0
*/
public function isEncrypted();
/**
* Get the metadata etag for the file
*
* @return string | null
* @since 18.0.0
*/
public function getMetadataEtag(): ?string;
/**
* Get the last modified date as unix timestamp
*
* @return int | null
* @since 18.0.0
*/
public function getCreationTime(): ?int;
/**
* Get the last modified date as unix timestamp
*
* @return int | null
* @since 18.0.0
*/
public function getUploadTime(): ?int;
}

@ -707,6 +707,67 @@ class CacheTest extends \Test\TestCase {
}
}
public function testExtended() {
$folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory'];
$this->cache->put("", $folderData);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'creation_time' => 20];
$id1 = $this->cache->put("foo1", $data);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'upload_time' => 30];
$this->cache->put("foo2", $data);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'metadata_etag' => 'foo'];
$this->cache->put("foo3", $data);
$data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
$this->cache->put("foo4", $data);
$entry = $this->cache->get($id1);
$this->assertEquals(20, $entry->getCreationTime());
$this->assertEquals(0, $entry->getUploadTime());
$this->assertEquals(null, $entry->getMetadataEtag());
$entries = $this->cache->getFolderContents("");
$this->assertCount(4, $entries);
$this->assertEquals("foo1", $entries[0]->getName());
$this->assertEquals("foo2", $entries[1]->getName());
$this->assertEquals("foo3", $entries[2]->getName());
$this->assertEquals("foo4", $entries[3]->getName());
$this->assertEquals(20, $entries[0]->getCreationTime());
$this->assertEquals(0, $entries[0]->getUploadTime());
$this->assertEquals(null, $entries[0]->getMetadataEtag());
$this->assertEquals(0, $entries[1]->getCreationTime());
$this->assertEquals(30, $entries[1]->getUploadTime());
$this->assertEquals(null, $entries[1]->getMetadataEtag());
$this->assertEquals(0, $entries[2]->getCreationTime());
$this->assertEquals(0, $entries[2]->getUploadTime());
$this->assertEquals('foo', $entries[2]->getMetadataEtag());
$this->assertEquals(0, $entries[3]->getCreationTime());
$this->assertEquals(0, $entries[3]->getUploadTime());
$this->assertEquals(null, $entries[3]->getMetadataEtag());
$this->cache->update($id1, ['upload_time' => 25]);
$entry = $this->cache->get($id1);
$this->assertEquals(20, $entry->getCreationTime());
$this->assertEquals(25, $entry->getUploadTime());
$this->assertEquals(null, $entry->getMetadataEtag());
$this->cache->put("sub", $folderData);
$this->cache->move("foo1", "sub/foo1");
$entries = $this->cache->getFolderContents("sub");
$this->assertCount(1, $entries);
$this->assertEquals(20, $entries[0]->getCreationTime());
$this->assertEquals(25, $entries[0]->getUploadTime());
$this->assertEquals(null, $entries[0]->getMetadataEtag());
}
protected function tearDown() {
if ($this->cache) {
$this->cache->clear();

Loading…
Cancel
Save