Skip to content

Commit e8803f7

Browse files
authored
allow like clauses without wildcards (piccolo-orm#192)
It previously wasn't possible to do a LIKE clause with no wildcards. So if you just wanted to match on 'FOO', 'Foo', and 'foo', and not 'foo bar' or 'baz foo'.
1 parent 9824866 commit e8803f7

File tree

4 files changed

+144
-77
lines changed

4 files changed

+144
-77
lines changed

docs/src/piccolo/query_clauses/where.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ The percentage operator is required to designate where the match should occur.
7272
b.name.like('%is%') # Matches anywhere in string
7373
).run_sync()
7474
75-
``ilike`` is identical, except it's case insensitive.
75+
b.select().where(
76+
b.name.like('Pythonistas') # Matches the entire string
77+
).run_sync()
78+
79+
``ilike`` is identical, except it's Postgres specific and case insensitive.
7680

7781
-------------------------------------------------------------------------------
7882

piccolo/columns/base.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -424,25 +424,38 @@ def not_in(self, values: t.List[t.Any]) -> Where:
424424
return Where(column=self, values=values, operator=NotIn)
425425

426426
def like(self, value: str) -> Where:
427-
if "%" not in value:
428-
raise ValueError("% is required for like operators")
427+
"""
428+
Both SQLite and Postgres support LIKE, but they mean different things.
429+
430+
In Postgres, LIKE is case sensitive (i.e. 'foo' equals 'foo', but
431+
'foo' doesn't equal 'Foo').
432+
433+
In SQLite, LIKE is case insensitive for ASCII characters
434+
(i.e. 'foo' equals 'Foo'). But not for non-ASCII characters. To learn
435+
more, see the docs:
436+
437+
https://sqlite.org/lang_expr.html#the_like_glob_regexp_and_match_operators
438+
439+
"""
429440
return Where(column=self, value=value, operator=Like)
430441

431442
def ilike(self, value: str) -> Where:
432-
if "%" not in value:
433-
raise ValueError("% is required for ilike operators")
443+
"""
444+
Only Postgres supports ILIKE. It's used for case insensitive matching.
445+
446+
For SQLite, it's just proxied to a LIKE query instead.
447+
448+
"""
434449
if self._meta.engine_type == "postgres":
435450
operator: t.Type[ComparisonOperator] = ILike
436451
else:
437452
colored_warning(
438-
"SQLite doesn't support ILIKE currently, falling back to LIKE."
453+
"SQLite doesn't support ILIKE, falling back to LIKE."
439454
)
440455
operator = Like
441456
return Where(column=self, value=value, operator=operator)
442457

443458
def not_like(self, value: str) -> Where:
444-
if "%" not in value:
445-
raise ValueError("% is required for like operators")
446459
return Where(column=self, value=value, operator=NotLike)
447460

448461
def __lt__(self, value) -> Where:

tests/columns/test_base.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,6 @@ class MyTable(Table):
1010
name = Varchar()
1111

1212

13-
class TestColumn(TestCase):
14-
def test_like_raises(self):
15-
"""
16-
Make sure an invalid 'like' argument raises an exception. Should
17-
contain a % symbol.
18-
"""
19-
column = MyTable.name
20-
with self.assertRaises(ValueError):
21-
column.like("guido")
22-
23-
with self.assertRaises(ValueError):
24-
column.ilike("guido")
25-
26-
# Make sure valid args don't raise an exception.
27-
for arg in ["%guido", "guido%", "%guido%"]:
28-
column.like("%foo")
29-
column.ilike("foo%")
30-
31-
3213
class TestCopy(TestCase):
3314
def test_copy(self):
3415
"""

0 commit comments

Comments
 (0)