diff --git a/apps/files_sharing/lib/sharedpropagator.php b/apps/files_sharing/lib/sharedpropagator.php index 8de90b9552..f17cfc3a75 100644 --- a/apps/files_sharing/lib/sharedpropagator.php +++ b/apps/files_sharing/lib/sharedpropagator.php @@ -33,7 +33,6 @@ class SharedPropagator extends Propagator { * @param string $internalPath * @param int $time * @param int $sizeDifference - * @return \array[] all propagated entries */ public function propagateChange($internalPath, $time, $sizeDifference = 0) { /** @var \OC\Files\Storage\Storage $storage */ diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 48ab3fad5f..1a040364f1 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -332,7 +332,7 @@ class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage { if (!$storage) { $storage = $this; } - return new \OCA\Files_Sharing\SharedPropagator($storage); + return new \OCA\Files_Sharing\SharedPropagator($storage, \OC::$server->getDatabaseConnection()); } public function getOwner($path) { diff --git a/lib/private/DB/AdapterSqlite.php b/lib/private/DB/AdapterSqlite.php index d7769238ab..3466e0e1aa 100644 --- a/lib/private/DB/AdapterSqlite.php +++ b/lib/private/DB/AdapterSqlite.php @@ -32,6 +32,7 @@ class AdapterSqlite extends Adapter { $statement = preg_replace('/`(\w+)` ILIKE \?/', 'LOWER($1) LIKE LOWER(?)', $statement); $statement = str_replace( '`', '"', $statement ); $statement = str_ireplace( 'NOW()', 'datetime(\'now\')', $statement ); + $statement = str_ireplace('GREATEST(', 'MAX(', $statement); $statement = str_ireplace( 'UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $statement ); return $statement; } diff --git a/lib/private/Files/Cache/HomePropagator.php b/lib/private/Files/Cache/HomePropagator.php index 8edca9c0c8..db9e30ae0f 100644 --- a/lib/private/Files/Cache/HomePropagator.php +++ b/lib/private/Files/Cache/HomePropagator.php @@ -21,14 +21,16 @@ namespace OC\Files\Cache; +use OCP\IDBConnection; + class HomePropagator extends Propagator { private $ignoredBaseFolders; /** * @param \OC\Files\Storage\Storage $storage */ - public function __construct(\OC\Files\Storage\Storage $storage) { - parent::__construct($storage); + public function __construct(\OC\Files\Storage\Storage $storage, IDBConnection $connection) { + parent::__construct($storage, $connection); $this->ignoredBaseFolders = ['files_encryption']; } @@ -37,14 +39,13 @@ class HomePropagator extends Propagator { * @param string $internalPath * @param int $time * @param int $sizeDifference number of bytes the file has grown - * @return array[] all propagated entries */ public function propagateChange($internalPath, $time, $sizeDifference = 0) { list($baseFolder) = explode('/', $internalPath, 2); if (in_array($baseFolder, $this->ignoredBaseFolders)) { return []; } else { - return parent::propagateChange($internalPath, $time, $sizeDifference); + parent::propagateChange($internalPath, $time, $sizeDifference); } } } diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php index 50264e54d4..b998c6bcfa 100644 --- a/lib/private/Files/Cache/Propagator.php +++ b/lib/private/Files/Cache/Propagator.php @@ -21,7 +21,9 @@ namespace OC\Files\Cache; +use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Cache\IPropagator; +use OCP\IDBConnection; /** * Propagate etags and mtimes within the storage @@ -33,10 +35,17 @@ class Propagator implements IPropagator { protected $storage; /** - * @param \OC\Files\Storage\Storage $storage + * @var IDBConnection */ - public function __construct(\OC\Files\Storage\Storage $storage) { + private $connection; + + /** + * @param \OC\Files\Storage\Storage $storage + * @param IDBConnection $connection + */ + public function __construct(\OC\Files\Storage\Storage $storage, IDBConnection $connection) { $this->storage = $storage; + $this->connection = $connection; } @@ -44,31 +53,49 @@ class Propagator implements IPropagator { * @param string $internalPath * @param int $time * @param int $sizeDifference number of bytes the file has grown - * @return array[] all propagated entries */ public function propagateChange($internalPath, $time, $sizeDifference = 0) { - $cache = $this->storage->getCache($internalPath); + $storageId = (int)$this->storage->getStorageCache()->getNumericId(); - $parentId = $cache->getParentId($internalPath); - $propagatedEntries = []; - while ($parentId !== -1) { - $entry = $cache->get($parentId); - $propagatedEntries[] = $entry; - if (!$entry) { - return $propagatedEntries; - } - $mtime = max($time, $entry['mtime']); + $parents = $this->getParents($internalPath); - if ($entry['size'] === -1) { - $newSize = -1; - } else { - $newSize = $entry['size'] + $sizeDifference; - } - $cache->update($parentId, ['mtime' => $mtime, 'etag' => $this->storage->getETag($entry['path']), 'size' => $newSize]); + $parentHashes = array_map('md5', $parents); + $etag = uniqid(); // since we give all folders the same etag we don't ask the storage for the etag - $parentId = $entry['parent']; + $builder = $this->connection->getQueryBuilder(); + $hashParams = array_map(function ($hash) use ($builder) { + return $builder->expr()->literal($hash); + }, $parentHashes); + + $builder->update('filecache') + ->set('mtime', $builder->createFunction('GREATEST(`mtime`, ' . $builder->createNamedParameter($time) . ')')) + ->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR)) + ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) + ->andWhere($builder->expr()->in('path_hash', $hashParams)); + + $builder->execute(); + + if ($sizeDifference !== 0) { + // we need to do size separably so we can ignore entries with uncalculated size + $builder = $this->connection->getQueryBuilder(); + $builder->update('filecache') + ->set('size', $builder->createFunction('`size` + ' . $builder->createNamedParameter($sizeDifference))) + ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) + ->andWhere($builder->expr()->in('path_hash', $hashParams)) + ->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT))); } - return $propagatedEntries; + $builder->execute(); + } + + protected function getParents($path) { + $parts = explode('/', $path); + $parent = ''; + $parents = []; + foreach ($parts as $part) { + $parents[] = $parent; + $parent = trim($parent . '/' . $part, '/'); + } + return $parents; } } diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 3a811b312c..6382dbd26b 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -352,7 +352,7 @@ abstract class Common implements Storage, ILockingStorage { $storage = $this; } if (!isset($storage->propagator)) { - $storage->propagator = new Propagator($storage); + $storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection()); } return $storage->propagator; } diff --git a/lib/private/Files/Storage/Home.php b/lib/private/Files/Storage/Home.php index 9b98f2f7e1..19ba217fa4 100644 --- a/lib/private/Files/Storage/Home.php +++ b/lib/private/Files/Storage/Home.php @@ -88,7 +88,7 @@ class Home extends Local implements \OCP\Files\IHomeStorage { $storage = $this; } if (!isset($this->propagator)) { - $this->propagator = new HomePropagator($storage); + $this->propagator = new HomePropagator($storage, \OC::$server->getDatabaseConnection()); } return $this->propagator; } diff --git a/lib/public/files/cache/ipropagator.php b/lib/public/files/cache/ipropagator.php index 9730601d57..5494ec9a54 100644 --- a/lib/public/files/cache/ipropagator.php +++ b/lib/public/files/cache/ipropagator.php @@ -30,7 +30,6 @@ interface IPropagator { /** * @param string $internalPath * @param int $time - * @return array[] all propagated cache entries * @since 9.0.0 */ public function propagateChange($internalPath, $time);