From 9e450d727a2374c218cdd62c1c97b5ad7ebf48a8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 6 Nov 2019 11:38:47 +0100 Subject: [PATCH 1/2] add LEAST and GREATER to db function builder Signed-off-by: Robin Appelman --- .../FunctionBuilder/FunctionBuilder.php | 8 +++++ .../FunctionBuilder/SqliteFunctionBuilder.php | 9 ++++++ .../DB/QueryBuilder/IFunctionBuilder.php | 30 +++++++++++++++++++ .../DB/QueryBuilder/FunctionBuilderTest.php | 20 +++++++++++++ 4 files changed, 67 insertions(+) diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index 46bb536dfd..ffa758e4da 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -85,4 +85,12 @@ class FunctionBuilder implements IFunctionBuilder { public function min($field) { return new QueryFunction('MIN(' . $this->helper->quoteColumnName($field) . ')'); } + + public function greatest($x, $y) { + return new QueryFunction('GREATEST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); + } + + public function least($x, $y) { + return new QueryFunction('LEAST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); + } } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php index 21898cf3f9..f37ac20eca 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php @@ -30,4 +30,13 @@ class SqliteFunctionBuilder extends FunctionBuilder { public function concat($x, $y) { return new QueryFunction('(' . $this->helper->quoteColumnName($x) . ' || ' . $this->helper->quoteColumnName($y) . ')'); } + + public function greatest($x, $y) { + return new QueryFunction('MAX(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); + } + + public function least($x, $y) { + return new QueryFunction('MIN(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); + } + } diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php index 861a576914..d82d3ada8c 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -109,6 +109,8 @@ interface IFunctionBuilder { /** * Takes the maximum of all rows in a column * + * If you want to get the maximum value of multiple columns in the same row, use `greatest` instead + * * @param mixed $field the column to maximum * * @return IQueryFunction @@ -119,10 +121,38 @@ interface IFunctionBuilder { /** * Takes the minimum of all rows in a column * + * If you want to get the minimum value of multiple columns in the same row, use `least` instead + * * @param mixed $field the column to minimum * * @return IQueryFunction * @since 18.0.0 */ public function min($field); + + /** + * Takes the maximum of multiple values + * + * If you want to get the maximum value of all rows in a column, use `max` instead + * + * @param mixed $x the first input field or number + * @param mixed $y the first input field or number + * + * @return IQueryFunction + * @since 18.0.0 + */ + public function greatest($x, $y); + + /** + * Takes the minimum of multiple values + * + * If you want to get the minimum value of all rows in a column, use `min` instead + * + * @param mixed $x the first input field or number + * @param mixed $y the first input field or number + * + * @return IQueryFunction + * @since 18.0.0 + */ + public function least($x, $y); } diff --git a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php index a8af7f4fe0..0ddb50272b 100644 --- a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php +++ b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php @@ -198,4 +198,24 @@ class FunctionBuilderTest extends TestCase { $this->assertEquals(10, $query->execute()->fetchColumn()); } + + public function testGreatest() { + $query = $this->connection->getQueryBuilder(); + + $query->select($query->func()->greatest($query->createNamedParameter(2, IQueryBuilder::PARAM_INT), new Literal(1))); + $query->from('appconfig') + ->setMaxResults(1); + + $this->assertEquals(2, $query->execute()->fetchColumn()); + } + + public function testLeast() { + $query = $this->connection->getQueryBuilder(); + + $query->select($query->func()->least($query->createNamedParameter(2, IQueryBuilder::PARAM_INT), new Literal(1))); + $query->from('appconfig') + ->setMaxResults(1); + + $this->assertEquals(1, $query->execute()->fetchColumn()); + } } From 74c6beb603f18d51d92730bb631bfe77d8feb400 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 6 Nov 2019 12:05:46 +0100 Subject: [PATCH 2/2] dont set folder size to negative values during propagation normally this shouldn't be a problem, but cache/storage desync might cause this so this adds some failsafe to ensure we dont corrupt the cache further the minimum value is set to -1 instead of 0 in order to triger a background scan on the folder and figure out the size properly Signed-off-by: Robin Appelman --- lib/private/Files/Cache/Propagator.php | 7 +++++-- tests/lib/Files/Cache/PropagatorTest.php | 11 +++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php index 989a4d0c7d..41b4c2bb07 100644 --- a/lib/private/Files/Cache/Propagator.php +++ b/lib/private/Files/Cache/Propagator.php @@ -91,7 +91,7 @@ class Propagator implements IPropagator { }, $parentHashes); $builder->update('filecache') - ->set('mtime', $builder->createFunction('GREATEST(' . $builder->getColumnName('mtime') . ', ' . $builder->createNamedParameter((int)$time, IQueryBuilder::PARAM_INT) . ')')) + ->set('mtime', $builder->func()->greatest('mtime', $builder->createNamedParameter((int)$time, IQueryBuilder::PARAM_INT))) ->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)); @@ -102,7 +102,10 @@ class Propagator implements IPropagator { // we need to do size separably so we can ignore entries with uncalculated size $builder = $this->connection->getQueryBuilder(); $builder->update('filecache') - ->set('size', $builder->func()->add('size', $builder->createNamedParameter($sizeDifference))) + ->set('size', $builder->func()->greatest( + $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT), + $builder->func()->add('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))); diff --git a/tests/lib/Files/Cache/PropagatorTest.php b/tests/lib/Files/Cache/PropagatorTest.php index 402b29c8c3..bcd808a840 100644 --- a/tests/lib/Files/Cache/PropagatorTest.php +++ b/tests/lib/Files/Cache/PropagatorTest.php @@ -81,6 +81,17 @@ class PropagatorTest extends TestCase { } } + public function testSizePropagationNoNegative() { + $paths = ['', 'foo', 'foo/bar']; + $oldInfos = $this->getFileInfos($paths); + $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time(), -100); + $newInfos = $this->getFileInfos($paths); + + foreach ($oldInfos as $i => $oldInfo) { + $this->assertEquals(-1, $newInfos[$i]->getSize()); + } + } + public function testBatchedPropagation() { $this->storage->mkdir('foo/baz'); $this->storage->mkdir('asd');