Skip to content

Commit 727772c

Browse files
committed
feat: Add support for NOT ILIKE in query builder expressions
Signed-off-by: Kostiantyn Miakshyn <molodchick@gmail.com>
1 parent 88e5f26 commit 727772c

8 files changed

Lines changed: 133 additions & 0 deletions

File tree

lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,26 @@ public function notLike($x, $y, $type = null): string {
315315
return $this->expressionBuilder->notLike($x, $y);
316316
}
317317

318+
/**
319+
* Creates a NOT ILIKE() comparison expression with the given arguments.
320+
*
321+
* @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by NOT ILIKE() comparison.
322+
* @param ILiteral|IParameter|IQueryFunction|string $y Argument to be used in NOT ILIKE() comparison.
323+
* @param int|string|null $type one of the IQueryBuilder::PARAM_* constants
324+
* required when comparing text fields for oci compatibility
325+
*
326+
* @return string
327+
* @since 35.0.0
328+
*/
329+
#[\Override]
330+
public function notILike(
331+
string|IParameter|ILiteral|IQueryFunction $x,
332+
string|IParameter|ILiteral|IQueryFunction $y,
333+
int|string|null $type = null,
334+
): string {
335+
return $this->expressionBuilder->notLike((string)$this->functionBuilder->lower($x), (string)$this->functionBuilder->lower($y));
336+
}
337+
318338
/**
319339
* Creates a IN () comparison expression with the given arguments.
320340
*

lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
use OC\DB\ConnectionAdapter;
1212
use OC\DB\QueryBuilder\QueryFunction;
13+
use OCP\DB\QueryBuilder\ILiteral;
14+
use OCP\DB\QueryBuilder\IParameter;
1315
use OCP\DB\QueryBuilder\IQueryBuilder;
1416
use OCP\DB\QueryBuilder\IQueryFunction;
1517
use Psr\Log\LoggerInterface;
@@ -34,6 +36,17 @@ public function iLike($x, $y, $type = null): string {
3436
return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->collation . ' LIKE', $y);
3537
}
3638

39+
#[\Override]
40+
public function notILike(
41+
string|IParameter|ILiteral|IQueryFunction $x,
42+
string|IParameter|ILiteral|IQueryFunction $y,
43+
int|string|null $type = null,
44+
): string {
45+
$x = $this->helper->quoteColumnName($x);
46+
$y = $this->helper->quoteColumnName($y);
47+
return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->collation . ' NOT LIKE', $y);
48+
}
49+
3750
/**
3851
* Returns a IQueryFunction that casts the column to the given type
3952
*

lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,13 @@ public function like($x, $y, $type = null): string {
140140
public function iLike($x, $y, $type = null): string {
141141
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
142142
}
143+
144+
#[\Override]
145+
public function notILike(
146+
string|IParameter|ILiteral|IQueryFunction $x,
147+
string|IParameter|ILiteral|IQueryFunction $y,
148+
int|string|null $type = null,
149+
): string {
150+
return $this->notLike($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
151+
}
143152
}

lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,15 @@ public function iLike($x, $y, $type = null): string {
5757
$y = $this->helper->quoteColumnName($y);
5858
return $this->expressionBuilder->comparison($x, 'ILIKE', $y);
5959
}
60+
61+
#[\Override]
62+
public function notILike(
63+
string|IParameter|ILiteral|IQueryFunction $x,
64+
string|IParameter|ILiteral|IQueryFunction $y,
65+
int|string|null $type = null,
66+
): string {
67+
$x = $this->helper->quoteColumnName($x);
68+
$y = $this->helper->quoteColumnName($y);
69+
return $this->expressionBuilder->comparison($x, 'NOT ILIKE', $y);
70+
}
6071
}

lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ public function iLike($x, $y, $type = null): string {
2727
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type);
2828
}
2929

30+
#[\Override]
31+
public function notILike(
32+
string|IParameter|ILiteral|IQueryFunction $x,
33+
string|IParameter|ILiteral|IQueryFunction $y,
34+
int|string|null $type = null,
35+
): string {
36+
return $this->notLike($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type);
37+
}
38+
3039
/**
3140
* @param mixed $column
3241
* @param mixed|null $type

lib/public/DB/QueryBuilder/IExpressionBuilder.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,24 @@ public function notLike($x, $y, $type = null): string;
315315
*/
316316
public function iLike($x, $y, $type = null): string;
317317

318+
/**
319+
* Creates a NOT ILIKE() comparison expression with the given arguments.
320+
*
321+
* @param ILiteral|IParameter|IQueryFunction|string $x Field in string format to be inspected by NOT LIKE() comparison.
322+
* @param ILiteral|IParameter|IQueryFunction|string $y Argument to be used in NOT ILIKE() comparison.
323+
* @param IQueryBuilder::PARAM_*|null $type one of the IQueryBuilder::PARAM_* constants
324+
* required when comparing text fields for oci compatibility
325+
*
326+
*
327+
* @return string
328+
* @since 35.0.0
329+
*
330+
* @psalm-taint-sink sql $x
331+
* @psalm-taint-sink sql $y
332+
* @psalm-taint-sink sql $type
333+
*/
334+
public function notILike(string|IParameter|ILiteral|IQueryFunction $x, string|IParameter|ILiteral|IQueryFunction $y, int|string|null $type = null): string;
335+
318336
/**
319337
* Creates a IN () comparison expression with the given arguments.
320338
*

tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,43 @@ public function testILike($param1, $param2, $match): void {
103103
$this->assertEquals($match, $column);
104104
}
105105

106+
public static function notILikeProvider(): array {
107+
$connection = Server::get(IDBConnection::class);
108+
109+
return [
110+
['foo', 'bar', true],
111+
['foo', 'foo', false],
112+
['foo', 'Foo', false],
113+
['foo', 'f%', false],
114+
['foo', '%o', false],
115+
['foo', '%', false],
116+
['foo', 'fo_', false],
117+
['foo', 'foo_', true],
118+
['foo', $connection->escapeLikeParameter('fo_'), true],
119+
['foo', $connection->escapeLikeParameter('f%'), true],
120+
];
121+
}
122+
123+
/**
124+
*
125+
* @param string $param1
126+
* @param string $param2
127+
* @param boolean $match
128+
*/
129+
#[\PHPUnit\Framework\Attributes\DataProvider('notILikeProvider')]
130+
public function testNotILike($param1, $param2, $match): void {
131+
$query = $this->connection->getQueryBuilder();
132+
133+
$query->select(new Literal('1'))
134+
->from('users')
135+
->where($query->expr()->notILike($query->createNamedParameter($param1), $query->createNamedParameter($param2)));
136+
137+
$result = $query->executeQuery();
138+
$column = $result->fetchOne();
139+
$result->closeCursor();
140+
$this->assertEquals($match, $column);
141+
}
142+
106143
public function testCastColumn(): void {
107144
$appId = $this->getUniqueID('testing');
108145
$this->createConfig($appId, '1', '4');

tests/lib/DB/QueryBuilder/ExpressionBuilderTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,18 @@ public function testNotLike(string $input, bool $isLiteral): void {
194194
);
195195
}
196196

197+
public function testILike(): void {
198+
// iLike is implemented using lower() on both sides, so we just verify it returns a string
199+
$result = $this->expressionBuilder->iLike('test', 'value');
200+
$this->assertIsString($result);
201+
}
202+
203+
public function testNotILike(): void {
204+
// notILike is implemented using notLike with lower() on both sides, so we just verify it returns a string
205+
$result = $this->expressionBuilder->notILike('test', 'value');
206+
$this->assertIsString($result);
207+
}
208+
197209
public static function dataIn(): array {
198210
return [
199211
['value', false],
@@ -294,6 +306,10 @@ public static function dataClobComparisons(): array {
294306
['like', 'under\_%', IQueryBuilder::PARAM_STR, false, 1],
295307
['notLike', '%5%', IQueryBuilder::PARAM_STR, false, 8],
296308
['notLike', '%5%', IQueryBuilder::PARAM_STR, true, 6],
309+
['iLike', '%5%', IQueryBuilder::PARAM_STR, false, 3],
310+
['iLike', '%5%', IQueryBuilder::PARAM_STR, true, 1],
311+
['notILike', '%5%', IQueryBuilder::PARAM_STR, false, 8],
312+
['notILike', '%5%', IQueryBuilder::PARAM_STR, true, 6],
297313
['in', ['5'], IQueryBuilder::PARAM_STR_ARRAY, false, 3],
298314
['in', ['5'], IQueryBuilder::PARAM_STR_ARRAY, true, 1],
299315
['notIn', ['5'], IQueryBuilder::PARAM_STR_ARRAY, false, 8],

0 commit comments

Comments
 (0)