From d111e88d4c0c68661439e2323c953ef6d28669f0 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 4 Nov 2020 17:02:23 +0100 Subject: [PATCH 1/4] Fix order of GREATEST for Oracle As per https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions060.htm Oracle uses the first value to cast the rest or the values. So when the first value is a plain int, instead of doing the math, it will cast the expression to int and continue with a potential 0. Signed-off-by: Joas Schilling --- lib/private/Files/Cache/Propagator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php index c9200d33b1..53e56c3773 100644 --- a/lib/private/Files/Cache/Propagator.php +++ b/lib/private/Files/Cache/Propagator.php @@ -104,9 +104,9 @@ class Propagator implements IPropagator { $builder = $this->connection->getQueryBuilder(); $builder->update('filecache') ->set('size', $builder->func()->greatest( - $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT), - $builder->func()->add('size', $builder->createNamedParameter($sizeDifference))) - ) + $builder->func()->add('size', $builder->createNamedParameter($sizeDifference)), + $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) + )) ->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))); From c71cc6824e6902b94fb8117814f32ace74f143d2 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 6 Nov 2020 08:44:46 +0100 Subject: [PATCH 2/4] Use Query builder function Signed-off-by: Joas Schilling --- lib/private/Files/Cache/Propagator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php index 53e56c3773..97eaa839cc 100644 --- a/lib/private/Files/Cache/Propagator.php +++ b/lib/private/Files/Cache/Propagator.php @@ -168,7 +168,7 @@ class Propagator implements IPropagator { $storageId = (int)$this->storage->getStorageCache()->getNumericId(); $query->update('filecache') - ->set('mtime', $query->createFunction('GREATEST(' . $query->getColumnName('mtime') . ', ' . $query->createParameter('time') . ')')) + ->set('mtime', $query->func()->greatest('mtime', $query->createParameter('time'))) ->set('etag', $query->expr()->literal(uniqid())) ->where($query->expr()->eq('storage', $query->expr()->literal($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->eq('path_hash', $query->createParameter('hash'))); From 2e3cffc53bc4cc98833b6998b19c908e79b2b358 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 6 Nov 2020 08:52:09 +0100 Subject: [PATCH 3/4] Make sure Oracle always casts everything in the best way Signed-off-by: Joas Schilling --- .../FunctionBuilder/OCIFunctionBuilder.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php index f9347210c1..556ef22144 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php @@ -24,9 +24,52 @@ namespace OC\DB\QueryBuilder\FunctionBuilder; use OC\DB\QueryBuilder\QueryFunction; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; +use OCP\DB\QueryBuilder\IQueryFunction; class OCIFunctionBuilder extends FunctionBuilder { public function md5($input) { return new QueryFunction('LOWER(DBMS_OBFUSCATION_TOOLKIT.md5 (input => UTL_RAW.cast_to_raw(' . $this->helper->quoteColumnName($input) .')))'); } + + /** + * As per https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions060.htm + * Oracle uses the first value to cast the rest or the values. So when the + * first value is a literal, plain value or column, instead of doing the + * math, it will cast the expression to int and continue with a "0". So when + * the second parameter is a function or column, we have to put that as + * first parameter. + * + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y + * @return QueryFunction + */ + public function greatest($x, $y) { + if (is_string($y) || $y instanceof IQueryFunction) { + return parent::greatest($y, $x); + } + + return parent::greatest($x, $y); + } + + /** + * As per https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions060.htm + * Oracle uses the first value to cast the rest or the values. So when the + * first value is a literal, plain value or column, instead of doing the + * math, it will cast the expression to int and continue with a "0". So when + * the second parameter is a function or column, we have to put that as + * first parameter. + * + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y + * @return QueryFunction + */ + public function least($x, $y) { + if (is_string($y) || $y instanceof IQueryFunction) { + return parent::least($y, $x); + } + + return parent::least($x, $y); + } } From b3dfa9290d176174cc3a56a216f54883e2b8e6d7 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Sat, 7 Nov 2020 14:06:03 +0100 Subject: [PATCH 4/4] Update parameters Signed-off-by: Joas Schilling --- .../FunctionBuilder/FunctionBuilder.php | 13 +++++++++++++ .../FunctionBuilder/OCIFunctionBuilder.php | 4 ++-- .../FunctionBuilder/SqliteFunctionBuilder.php | 13 +++++++++++++ lib/public/DB/QueryBuilder/IFunctionBuilder.php | 10 ++++------ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index a49edc505f..23a834a9c4 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -27,6 +27,9 @@ namespace OC\DB\QueryBuilder\FunctionBuilder; use OC\DB\QueryBuilder\QueryFunction; use OC\DB\QueryBuilder\QuoteHelper; use OCP\DB\QueryBuilder\IFunctionBuilder; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; +use OCP\DB\QueryBuilder\IQueryFunction; class FunctionBuilder implements IFunctionBuilder { /** @var QuoteHelper */ @@ -87,10 +90,20 @@ class FunctionBuilder implements IFunctionBuilder { return new QueryFunction('MIN(' . $this->helper->quoteColumnName($field) . ')'); } + /** + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y + * @return IQueryFunction + */ public function greatest($x, $y) { return new QueryFunction('GREATEST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); } + /** + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y + * @return IQueryFunction + */ 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/OCIFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php index 556ef22144..aec9257458 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php @@ -43,7 +43,7 @@ class OCIFunctionBuilder extends FunctionBuilder { * * @param string|ILiteral|IParameter|IQueryFunction $x * @param string|ILiteral|IParameter|IQueryFunction $y - * @return QueryFunction + * @return IQueryFunction */ public function greatest($x, $y) { if (is_string($y) || $y instanceof IQueryFunction) { @@ -63,7 +63,7 @@ class OCIFunctionBuilder extends FunctionBuilder { * * @param string|ILiteral|IParameter|IQueryFunction $x * @param string|ILiteral|IParameter|IQueryFunction $y - * @return QueryFunction + * @return IQueryFunction */ public function least($x, $y) { if (is_string($y) || $y instanceof IQueryFunction) { diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php index 6d8e947c40..f759fb22fd 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php @@ -25,16 +25,29 @@ namespace OC\DB\QueryBuilder\FunctionBuilder; use OC\DB\QueryBuilder\QueryFunction; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; +use OCP\DB\QueryBuilder\IQueryFunction; class SqliteFunctionBuilder extends FunctionBuilder { public function concat($x, $y) { return new QueryFunction('(' . $this->helper->quoteColumnName($x) . ' || ' . $this->helper->quoteColumnName($y) . ')'); } + /** + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y + * @return IQueryFunction + */ public function greatest($x, $y) { return new QueryFunction('MAX(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); } + /** + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y + * @return IQueryFunction + */ 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 e744d653d7..aa11e42d62 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -137,9 +137,8 @@ interface IFunctionBuilder { * * 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 - * + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y * @return IQueryFunction * @since 18.0.0 */ @@ -150,9 +149,8 @@ interface IFunctionBuilder { * * 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 - * + * @param string|ILiteral|IParameter|IQueryFunction $x + * @param string|ILiteral|IParameter|IQueryFunction $y * @return IQueryFunction * @since 18.0.0 */