diff --git a/gluon/dal.py b/gluon/dal.py index 71d220a9..9397706c 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -1399,12 +1399,13 @@ class BaseAdapter(ConnectionPool): def LIKE(self, first, second): """Case sensitive like operator""" - raise NotImplementedError + return '(%s LIKE %s)' % (self.expand(first), + self.expand(second, 'string')) def ILIKE(self, first, second): """Case insensitive like operator""" - return '(%s LIKE %s)' % (self.expand(first), - self.expand(second, 'string')) + return '(LOWER(%s) LIKE %s)' % (self.expand(first), + self.expand(second, 'string').lower()) def STARTSWITH(self, first, second): return '(%s LIKE %s)' % (self.expand(first), @@ -1414,7 +1415,7 @@ class BaseAdapter(ConnectionPool): return '(%s LIKE %s)' % (self.expand(first), self.expand('%'+second, 'string')) - def CONTAINS(self, first, second, case_sensitive=False): + def CONTAINS(self, first, second, case_sensitive=True): if first.type in ('string', 'text', 'json'): if isinstance(second, Expression): second = Expression(None, self.CONCAT('%', Expression( @@ -2924,11 +2925,11 @@ class PostgreSQLAdapter(BaseAdapter): self.expand(second, 'string')) def STARTSWITH(self, first, second): - return '(%s ILIKE %s)' % (self.expand(first), + return '(%s LIKE %s)' % (self.expand(first), self.expand(second+'%', 'string')) def ENDSWITH(self, first, second): - return '(%s ILIKE %s)' % (self.expand(first), + return '(%s LIKE %s)' % (self.expand(first), self.expand('%'+second, 'string')) # GIS functions @@ -9739,7 +9740,7 @@ class Expression(object): db = self.db return Query(db, db._adapter.GE, self, value) - def like(self, value, case_sensitive=False): + def like(self, value, case_sensitive=True): db = self.db op = case_sensitive and db._adapter.LIKE or db._adapter.ILIKE return Query(db, op, self, value) diff --git a/gluon/tests/test_dal.py b/gluon/tests/test_dal.py index f3835a77..8c95b7af 100644 --- a/gluon/tests/test_dal.py +++ b/gluon/tests/test_dal.py @@ -381,14 +381,35 @@ class TestLike(unittest.TestCase): self.assertEqual(db(db.tt.aa.like('%b%')).count(), 1) self.assertEqual(db(db.tt.aa.like('%c')).count(), 1) self.assertEqual(db(db.tt.aa.like('%d%')).count(), 0) - self.assertEqual(db(db.tt.aa.lower().like('A%')).count(), 1) - self.assertEqual(db(db.tt.aa.lower().like('%B%')).count(), - 1) - self.assertEqual(db(db.tt.aa.lower().like('%C')).count(), 1) - self.assertEqual(db(db.tt.aa.upper().like('A%')).count(), 1) - self.assertEqual(db(db.tt.aa.upper().like('%B%')).count(), - 1) - self.assertEqual(db(db.tt.aa.upper().like('%C')).count(), 1) + #DAL maps like() (and contains(), startswith(), endswith()) + #to the LIKE operator, that in ANSI-SQL is case-sensitive + #There are backends supporting case-sensitivity by default + #and backends that needs additional care to turn + #case-sensitivity on. To discern among those, let's run + #this query comparing previously inserted 'abc' with 'ABC': + #if the result is 0, then the backend recognizes + #case-sensitivity, if 1 it isn't + is_case_insensitive = db(db.tt.aa.like('%ABC%')).count() + if is_case_insensitive: + self.assertEqual(db(db.tt.aa.like('A%')).count(), 1) + self.assertEqual(db(db.tt.aa.like('%B%')).count(), 1) + self.assertEqual(db(db.tt.aa.like('%C')).count(), 1) + self.assertEqual(db(db.tt.aa.like('A%', case_sensitive=False)).count(), 1) + self.assertEqual(db(db.tt.aa.like('%B%', case_sensitive=False)).count(), 1) + self.assertEqual(db(db.tt.aa.like('%C', case_sensitive=False)).count(), 1) + self.assertEqual(db(db.tt.aa.upper().like('A%')).count(), 1) + self.assertEqual(db(db.tt.aa.upper().like('%B%')).count(),1) + self.assertEqual(db(db.tt.aa.upper().like('%C')).count(), 1) + else: + self.assertEqual(db(db.tt.aa.like('A%')).count(), 0) + self.assertEqual(db(db.tt.aa.like('%B%')).count(), 0) + self.assertEqual(db(db.tt.aa.like('%C')).count(), 0) + self.assertEqual(db(db.tt.aa.like('A%', case_sensitive=False)).count(), 1) + self.assertEqual(db(db.tt.aa.like('%B%', case_sensitive=False)).count(), 1) + self.assertEqual(db(db.tt.aa.like('%C', case_sensitive=False)).count(), 1) + self.assertEqual(db(db.tt.aa.upper().like('A%')).count(), 1) + self.assertEqual(db(db.tt.aa.upper().like('%B%')).count(),1) + self.assertEqual(db(db.tt.aa.upper().like('%C')).count(), 1) db.tt.drop() db.define_table('tt', Field('aa', 'integer')) self.assertEqual(db.tt.insert(aa=1111111111), 1)