Index: trac/ticket/tests/query.py
===================================================================
--- trac/ticket/tests/query.py	(revision 4794)
+++ trac/ticket/tests/query.py	(working copy)
@@ -312,6 +312,20 @@
         self.assertEqual([], args)
         tickets = query.execute(Mock(href=self.env.href))
 
+    def test_constrained_allof_keywords(self):
+        query = Query.from_string(self.env, None,
+                                  'keywords~=foo bar',
+                                  order='id')
+        sql, args = query.get_sql()
+        self.assertEqual(sql,
+"""SELECT t.id AS id,t.summary AS summary,t.keywords AS keywords,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
+FROM ticket AS t
+  LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
+WHERE (COALESCE(t.keywords,'') LIKE %s ESCAPE '/' AND COALESCE(t.keywords,'') LIKE %s ESCAPE '/')
+ORDER BY COALESCE(t.id,0)=0,t.id""")
+        self.assertEqual(['%foo%', '%bar%'], args)
+        tickets = query.execute(Mock(href=self.env.href))
+
     def test_csv_escape(self):
         query = Mock(get_columns=lambda: ['col1'],
                      execute=lambda r,c: [{'col1': 'value, needs escaped'}])
@@ -336,7 +350,6 @@
                          '<em class="error">[Error: Query filter requires '
                          'field and constraints separated by a "="]</em>')
 
-
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(QueryTestCase, 'test'))
Index: trac/ticket/query.py
===================================================================
--- trac/ticket/query.py	(revision 4794)
+++ trac/ticket/query.py	(working copy)
@@ -301,11 +301,35 @@
 
             if mode == '':
                 return ("COALESCE(%s,'')%s=%%s" % (name, neg and '!' or ''),
-                        value)
+                        [value])
             if not value:
                 return None
             db = self.env.get_db_cnx()
             value = db.like_escape(value)
+
+            # special case - search for keywords separated by space
+            if mode == '~' and name == 't.keywords':
+                words = value.split(' ')
+                con = '('
+                count = 0
+                args = []
+                # iterate the words
+                for w in words:
+                    # word is empty, let's skip it
+                    if w == '':
+                        continue
+                    if count > 0:
+                        # need to append to the AND sequence
+                        con = con + ' AND ';
+                    con = con + "COALESCE(%s,'') %s" % (name, db.like())
+                    args.append('%' + w + '%')
+                    count = count + 1
+                if (count == 0):
+                    # there are no words to filter by
+                    return None
+                con = con + ')'
+                return (con, args)
+
             if mode == '~':
                 value = '%' + value + '%'
             elif mode == '^':
@@ -314,7 +338,7 @@
                 value = '%' + value
             return ("COALESCE(%s,'') %s%s" % (name, neg and 'NOT ' or '',
                                               db.like()),
-                    value)
+                    [value])
 
         clauses = []
         args = []
@@ -368,12 +392,13 @@
                 else:
                     clauses.append("(" + " OR ".join(
                         [item[0] for item in constraint_sql]) + ")")
-                args += [item[1] for item in constraint_sql]
+                for item in constraint_sql:
+                    args.extend(item[1])
             elif len(v) == 1:
                 constraint_sql = get_constraint_sql(k, v[0], mode, neg)
                 if constraint_sql:
                     clauses.append(constraint_sql[0])
-                    args.append(constraint_sql[1])
+                    args.extend(constraint_sql[1])
 
         clauses = filter(None, clauses)
         if clauses:

