diff --git a/trac/db/mysql_backend.py b/trac/db/mysql_backend.py
--- a/trac/db/mysql_backend.py
+++ b/trac/db/mysql_backend.py
@@ -214,6 +214,7 @@
         return 'concat(%s)' % ', '.join(args)
 
     def like(self):
+        """Return a case-insensitive LIKE clause."""
         return "LIKE %s COLLATE utf8_general_ci ESCAPE '/'"
 
     def like_escape(self, text):
diff --git a/trac/db/postgres_backend.py b/trac/db/postgres_backend.py
--- a/trac/db/postgres_backend.py
+++ b/trac/db/postgres_backend.py
@@ -197,8 +197,7 @@
         return '||'.join(args)
 
     def like(self):
-        # Temporary hack needed for the case-insensitive string matching in the
-        # search module
+        """Return a case-insensitive LIKE clause."""
         return "ILIKE %s ESCAPE '/'"
 
     def like_escape(self, text):
diff --git a/trac/db/sqlite_backend.py b/trac/db/sqlite_backend.py
--- a/trac/db/sqlite_backend.py
+++ b/trac/db/sqlite_backend.py
@@ -253,6 +253,7 @@
         return '||'.join(args)
 
     def like(self):
+        """Return a case-insensitive LIKE clause."""
         if sqlite_version >= 30100:
             return "LIKE %s ESCAPE '/'"
         else:
diff --git a/trac/ticket/model.py b/trac/ticket/model.py
--- a/trac/ticket/model.py
+++ b/trac/ticket/model.py
@@ -382,7 +382,7 @@
         cursor.execute("SELECT time,author FROM ticket_change "
                        "WHERE ticket=%%s AND field='comment' "
                        "  AND (oldvalue=%%s OR oldvalue %s)" % db.like(),
-                       (self.id, scnum, '%.' + db.like_escape(scnum)))
+                       (self.id, scnum, '%' + db.like_escape('.' + scnum)))
         for ts, author in cursor:
             cursor.execute("SELECT field,author,oldvalue,newvalue "
                            "FROM ticket_change "
@@ -403,18 +403,18 @@
         when_ts = to_timestamp(when)
         
         db, handle_ta = self._get_db_for_write(db)
-        like = db.like()
         cursor = db.cursor()
         cursor.execute("SELECT time,newvalue FROM ticket_change "
                        "WHERE ticket=%%s AND field='comment' "
-                       "  AND (oldvalue=%%s OR oldvalue %s)" % like,
-                       (self.id, scnum, '%.' + db.like_escape(scnum)))
+                       "  AND (oldvalue=%%s OR oldvalue %s)" % db.like(),
+                       (self.id, scnum, '%' + db.like_escape('.' + scnum)))
         for (ts, old_comment) in cursor:
             if comment == old_comment:
                 return
             cursor.execute("SELECT COUNT(ticket) FROM ticket_change "
-                           "WHERE ticket=%%s AND time=%%s AND field %s" % like,
-                           (self.id, ts, db.like_escape('_comment') + '%'))
+                           "WHERE ticket=%s AND time=%s "
+                           "  AND field>='_comment0' and field<'_comment:'",
+                           (self.id, ts))
             rev = cursor.fetchone()[0]
             cursor.execute("INSERT INTO ticket_change "
                            "(ticket,time,author,field,oldvalue,newvalue) "
@@ -431,18 +431,18 @@
     def get_comment_history(self, cnum, db=None):
         scnum = str(cnum)
         db = self._get_db(db)
-        like = db.like()
         history = []
         cursor = db.cursor()
         cursor.execute("SELECT time,author,newvalue FROM ticket_change "
                        "WHERE ticket=%%s AND field='comment' "
-                       "  AND (oldvalue=%%s OR oldvalue %s)" % like,
-                       (self.id, scnum, '%.' + db.like_escape(scnum)))
+                       "  AND (oldvalue=%%s OR oldvalue %s)" % db.like(),
+                       (self.id, scnum, '%' + db.like_escape('.' + scnum)))
         for (ts0, author0, last_comment) in cursor:
             cursor.execute("SELECT field,author,oldvalue,newvalue "
                            "FROM ticket_change "
-                           "WHERE ticket=%%s AND time=%%s AND field %s" % like,
-                           (self.id, ts0, db.like_escape('_comment') + '%'))
+                           "WHERE ticket=%s AND time=%s "
+                           "  AND field>='_comment0' and field<'_comment:'",
+                           (self.id, ts0))
             for field, author, comment, ts in cursor:
                 rev = int(field[8:])
                 history.append((rev, datetime.fromtimestamp(int(ts0), utc),
diff --git a/trac/ticket/tests/query.py b/trac/ticket/tests/query.py
--- a/trac/ticket/tests/query.py
+++ b/trac/ticket/tests/query.py
@@ -210,7 +210,7 @@
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
 WHERE ((COALESCE(t.owner,'') %(like)s))
-ORDER BY COALESCE(t.id,0)=0,t.id""" %  {'like': self.env.get_db_cnx().like()})
+ORDER BY COALESCE(t.id,0)=0,t.id""" % {'like': self.env.get_db_cnx().like()})
         self.assertEqual(['%someone%'], args)
         tickets = query.execute(self.req)
 
@@ -222,7 +222,7 @@
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
 WHERE ((COALESCE(t.owner,'') NOT %(like)s))
-ORDER BY COALESCE(t.id,0)=0,t.id""" %  {'like': self.env.get_db_cnx().like()})
+ORDER BY COALESCE(t.id,0)=0,t.id""" % {'like': self.env.get_db_cnx().like()})
         self.assertEqual(['%someone%'], args)
         tickets = query.execute(self.req)
 
@@ -234,7 +234,7 @@
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
 WHERE ((COALESCE(t.owner,'') %(like)s))
-ORDER BY COALESCE(t.id,0)=0,t.id""" %  {'like': self.env.get_db_cnx().like()})
+ORDER BY COALESCE(t.id,0)=0,t.id""" % {'like': self.env.get_db_cnx().like()})
         self.assertEqual(['someone%'], args)
         tickets = query.execute(self.req)
 
@@ -246,7 +246,7 @@
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
 WHERE ((COALESCE(t.owner,'') %(like)s))
-ORDER BY COALESCE(t.id,0)=0,t.id""" %  {'like': self.env.get_db_cnx().like()})
+ORDER BY COALESCE(t.id,0)=0,t.id""" % {'like': self.env.get_db_cnx().like()})
         self.assertEqual(['%someone'], args)
         tickets = query.execute(self.req)
 
@@ -423,7 +423,7 @@
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
 WHERE (((COALESCE(t.keywords,'') %(like)s AND COALESCE(t.keywords,'') NOT %(like)s AND COALESCE(t.keywords,'') %(like)s)))
-ORDER BY COALESCE(t.id,0)=0,t.id"""  % {'like': self.env.get_db_cnx().like()})
+ORDER BY COALESCE(t.id,0)=0,t.id""" % {'like': self.env.get_db_cnx().like()})
         self.assertEqual(['%foo%', '%bar%', '%baz%'], args)
         tickets = query.execute(self.req)
 
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
--- a/trac/versioncontrol/cache.py
+++ b/trac/versioncontrol/cache.py
@@ -245,9 +245,9 @@
         rev_as_int = db.cast('rev', 'int')
         if first is None:
             cursor.execute("SELECT rev FROM node_change "
-                           "WHERE path = %%s "
+                           "WHERE %s <= %%s "
+                           "  AND path = %%s "
                            "  AND change_type IN ('A', 'C', 'M') "
-                           "  AND %s <= %%s "
                            "ORDER BY %s DESC "
                            "LIMIT 1" % ((rev_as_int,) * 2),
                            (path, last))
@@ -255,10 +255,10 @@
             for row in cursor:
                 first = int(row[0])
         cursor.execute("SELECT DISTINCT rev FROM node_change "
-                       "WHERE (path = %%s OR path %s) "
-                       " AND %s >= %%s AND %s <= %%s" % 
-                       (db.like(), rev_as_int, rev_as_int),
-                       (path, db.like_escape(path + '/') + '%', first, last))
+                       "WHERE %s >= %%s AND %s <= %%s "
+                       "  AND (path = %%s OR (path >= %%s AND path < %%s))" %
+                       (rev_as_int, rev_as_int),
+                       (first, last, path, path + '/', path + '0'))
         return [int(row[0]) for row in cursor]
 
     def has_node(self, path, rev=None):
@@ -293,22 +293,15 @@
 
         if path:
             path = path.lstrip('/')
-            sql += " AND ("
-            # changes on path itself
-            sql += "path=%s "
-            args.append(path)
-            sql += " OR "
-            # changes on path children
-            sql += "path "+db.like()
-            args.append(db.like_escape(path+'/') + '%')
-            sql += " OR "
+            # changes on path itself or its children
+            sql += " AND (path = %s OR (path >= %s AND path < %s)"
+            args.extend((path, path + '/', path + '0'))
             # deletion of path ancestors
             components = path.lstrip('/').split('/')
-            for i in range(1, len(components)+1):
+            parents = ','.join(('%s',) * len(components))
+            sql += " OR (path IN (" + parents + ") AND change_type = 'D'))"
+            for i in range(1, len(components) + 1):
                 args.append('/'.join(components[:i]))
-            parent_insert = ','.join(('%s',) * len(components))
-            sql += " (path in (" + parent_insert + ") and change_type='D')"
-            sql += ")"
 
         sql += " ORDER BY " + db.cast('rev', 'int') + \
                 (direction == '<' and " DESC" or "") + " LIMIT 1"

