From 774bb066d97189a719f64c0d51eeffd187613fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 24 Mar 2022 11:12:12 +0100 Subject: [PATCH 1/5] Use getLengthExpression to measure field length instead of like MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/user_ldap/lib/Migration/Version1120Date20210917155206.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php b/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php index d2b440c4dfe94..87a8f07fbe122 100644 --- a/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php +++ b/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php @@ -127,9 +127,10 @@ protected function handleIDs(string $table, bool $emitHooks) { protected function getSelectQuery(string $table): IQueryBuilder { $qb = $this->dbc->getQueryBuilder(); + $lengthExpr = $this->dbc->getDatabasePlatform()->getLengthExpression('owncloud_name'); $qb->select('owncloud_name', 'directory_uuid') ->from($table) - ->where($qb->expr()->like('owncloud_name', $qb->createNamedParameter(str_repeat('_', 65) . '%'), Types::STRING)); + ->where($qb->expr()->gt($qb->createFunction($lengthExpr), '64', IQueryBuilder::PARAM_INT)); return $qb; } From 131a397472876303b643b6b7414b158889791927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 24 Mar 2022 16:17:37 +0100 Subject: [PATCH 2/5] Add octetLength and charLength to function builder, and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../Version1120Date20210917155206.php | 3 +- .../FunctionBuilder/FunctionBuilder.php | 12 +++++ .../FunctionBuilder/SqliteFunctionBuilder.php | 6 +++ .../DB/QueryBuilder/IFunctionBuilder.php | 18 ++++++++ .../DB/QueryBuilder/FunctionBuilderTest.php | 46 +++++++++++++++++++ 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php b/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php index 87a8f07fbe122..bca390441d70d 100644 --- a/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php +++ b/apps/user_ldap/lib/Migration/Version1120Date20210917155206.php @@ -127,10 +127,9 @@ protected function handleIDs(string $table, bool $emitHooks) { protected function getSelectQuery(string $table): IQueryBuilder { $qb = $this->dbc->getQueryBuilder(); - $lengthExpr = $this->dbc->getDatabasePlatform()->getLengthExpression('owncloud_name'); $qb->select('owncloud_name', 'directory_uuid') ->from($table) - ->where($qb->expr()->gt($qb->createFunction($lengthExpr), '64', IQueryBuilder::PARAM_INT)); + ->where($qb->expr()->gt($qb->func()->octetLength('owncloud_name'), '64', IQueryBuilder::PARAM_INT)); return $qb; } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index dfc0d0a6d6129..f0a809eff3575 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -79,6 +79,18 @@ public function count($count = '', $alias = ''): IQueryFunction { return new QueryFunction('COUNT(' . $quotedName . ')' . $alias); } + public function octetLength($field, $alias = ''): IQueryFunction { + $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; + $quotedName = $this->helper->quoteColumnName($field); + return new QueryFunction('LENGTHB(' . $quotedName . ')' . $alias); + } + + public function charLength($field, $alias = ''): IQueryFunction { + $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; + $quotedName = $this->helper->quoteColumnName($field); + return new QueryFunction('LENGTH(' . $quotedName . ')' . $alias); + } + public function max($field): IQueryFunction { return new QueryFunction('MAX(' . $this->helper->quoteColumnName($field) . ')'); } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php index 19cff52546bcc..9ad8483ea879e 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php @@ -38,4 +38,10 @@ public function greatest($x, $y): IQueryFunction { public function least($x, $y): IQueryFunction { return new QueryFunction('MIN(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); } + + public function octetLength($field, $alias = ''): IQueryFunction { + $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; + $quotedName = $this->helper->quoteColumnName($field); + return new QueryFunction('LENGTH(CAST(' . $quotedName . ' as BLOB))' . $alias); + } } diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php index 33d0fbe3b1a14..2f4df4d020b12 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -107,6 +107,24 @@ public function subtract($x, $y): IQueryFunction; */ public function count($count = '', $alias = ''): IQueryFunction; + /** + * @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured + * @param string $alias Alias for the length + * + * @return IQueryFunction + * @since 24.0.0 + */ + public function octetLength($field, $alias = ''): IQueryFunction; + + /** + * @param string|ILiteral|IParameter|IQueryFunction $field The input to be measured + * @param string $alias Alias for the length + * + * @return IQueryFunction + * @since 24.0.0 + */ + public function charLength($field, $alias = ''): IQueryFunction; + /** * Takes the maximum of all rows in a column * diff --git a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php index 71ae3d5c7f6b8..c7b549c9fb2dc 100644 --- a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php +++ b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php @@ -145,6 +145,52 @@ public function testCount() { $this->assertGreaterThan(1, $column); } + public function octetLengthProvider() { + return [ + ['', 0], + ['foobar', 6], + ['fé', 3], + ['šđčćž', 10], + ]; + } + + /** + * @dataProvider octetLengthProvider + */ + public function testOctetLength(string $str, int $bytes) { + $query = $this->connection->getQueryBuilder(); + + $query->select($query->func()->octetLength($query->createNamedParameter($str, IQueryBuilder::PARAM_STR))); + + $result = $query->execute(); + $column = $result->fetchOne(); + $result->closeCursor(); + $this->assertEquals($bytes, $column); + } + + public function charLengthProvider() { + return [ + ['', 0], + ['foobar', 6], + ['fé', 2], + ['šđčćž', 5], + ]; + } + + /** + * @dataProvider charLengthProvider + */ + public function testCharLength(string $str, int $bytes) { + $query = $this->connection->getQueryBuilder(); + + $query->select($query->func()->charLength($query->createNamedParameter($str, IQueryBuilder::PARAM_STR))); + + $result = $query->execute(); + $column = $result->fetchOne(); + $result->closeCursor(); + $this->assertEquals($bytes, $column); + } + private function setUpMinMax($value) { $query = $this->connection->getQueryBuilder(); From 3a61963980e114e5ed130dd17e8c5308d684d087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 24 Mar 2022 17:02:27 +0100 Subject: [PATCH 3/5] Avoid select with no FROM clause for Oracle in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- tests/lib/DB/QueryBuilder/FunctionBuilderTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php index c7b549c9fb2dc..8aac2c4a7fa0a 100644 --- a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php +++ b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php @@ -161,6 +161,8 @@ public function testOctetLength(string $str, int $bytes) { $query = $this->connection->getQueryBuilder(); $query->select($query->func()->octetLength($query->createNamedParameter($str, IQueryBuilder::PARAM_STR))); + $query->from('appconfig') + ->setMaxResults(1); $result = $query->execute(); $column = $result->fetchOne(); @@ -184,6 +186,8 @@ public function testCharLength(string $str, int $bytes) { $query = $this->connection->getQueryBuilder(); $query->select($query->func()->charLength($query->createNamedParameter($str, IQueryBuilder::PARAM_STR))); + $query->from('appconfig') + ->setMaxResults(1); $result = $query->execute(); $column = $result->fetchOne(); From 6e3cd43903159cf2a7b4574168f44f414bab29bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 24 Mar 2022 17:22:41 +0100 Subject: [PATCH 4/5] Use OCTET_LENGTH which is more common than LENGTHB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php | 2 +- .../DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index f0a809eff3575..5c494db7b62c1 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -82,7 +82,7 @@ public function count($count = '', $alias = ''): IQueryFunction { public function octetLength($field, $alias = ''): IQueryFunction { $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; $quotedName = $this->helper->quoteColumnName($field); - return new QueryFunction('LENGTHB(' . $quotedName . ')' . $alias); + return new QueryFunction('OCTET_LENGTH(' . $quotedName . ')' . $alias); } public function charLength($field, $alias = ''): IQueryFunction { diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php index 5581207a166d4..5c3c5d327d7be 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php @@ -72,4 +72,10 @@ public function least($x, $y): IQueryFunction { return parent::least($x, $y); } + + public function octetLength($field, $alias = ''): IQueryFunction { + $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; + $quotedName = $this->helper->quoteColumnName($field); + return new QueryFunction('LENGTHB(' . $quotedName . ')' . $alias); + } } From 6185e326dca7e825231041d9be434a3bc09c1031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 28 Mar 2022 00:01:17 +0200 Subject: [PATCH 5/5] Fix LENGTH function name across databases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php | 2 +- .../DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php | 6 ++++++ .../QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index 5c494db7b62c1..0f82f660badaa 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -88,7 +88,7 @@ public function octetLength($field, $alias = ''): IQueryFunction { public function charLength($field, $alias = ''): IQueryFunction { $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; $quotedName = $this->helper->quoteColumnName($field); - return new QueryFunction('LENGTH(' . $quotedName . ')' . $alias); + return new QueryFunction('CHAR_LENGTH(' . $quotedName . ')' . $alias); } public function max($field): IQueryFunction { diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php index 5c3c5d327d7be..e7d8ba8ce948b 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php @@ -78,4 +78,10 @@ public function octetLength($field, $alias = ''): IQueryFunction { $quotedName = $this->helper->quoteColumnName($field); return new QueryFunction('LENGTHB(' . $quotedName . ')' . $alias); } + + public function charLength($field, $alias = ''): IQueryFunction { + $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; + $quotedName = $this->helper->quoteColumnName($field); + return new QueryFunction('LENGTH(' . $quotedName . ')' . $alias); + } } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php index 9ad8483ea879e..1cff67fcdc0b7 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php @@ -44,4 +44,10 @@ public function octetLength($field, $alias = ''): IQueryFunction { $quotedName = $this->helper->quoteColumnName($field); return new QueryFunction('LENGTH(CAST(' . $quotedName . ' as BLOB))' . $alias); } + + public function charLength($field, $alias = ''): IQueryFunction { + $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; + $quotedName = $this->helper->quoteColumnName($field); + return new QueryFunction('LENGTH(' . $quotedName . ')' . $alias); + } }