[CalendarServer-changes] [7595] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Jun 14 13:53:30 PDT 2011


Revision: 7595
          http://trac.macosforge.org/projects/calendarserver/changeset/7595
Author:   cdaboo at apple.com
Date:     2011-06-14 13:53:29 -0700 (Tue, 14 Jun 2011)
Log Message:
-----------
Support extended query report.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/caldavxml.py
    CalendarServer/trunk/twistedcaldav/query/addressbookquery.py
    CalendarServer/trunk/twistedcaldav/query/calendarquery.py
    CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
    CalendarServer/trunk/twistedcaldav/query/expression.py
    CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/txdav/common/datastore/sql_legacy.py

Modified: CalendarServer/trunk/twistedcaldav/caldavxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/caldavxml.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/twistedcaldav/caldavxml.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -56,6 +56,10 @@
     "inbox-availability",
 )
 
+caldav_query_extended_compliance = (
+    "calendar-query-extended",
+)
+
 class CalDAVElement (davxml.WebDAVElement):
     """
     CalDAV XML element.
@@ -636,7 +640,10 @@
         (caldav_namespace, "comp-filter"    ): (0, None),
         (caldav_namespace, "prop-filter"    ): (0, None),
     }
-    allowed_attributes = { "name": True }
+    allowed_attributes = {
+        "name": True,
+        "test": False,
+    }
 
 class PropertyFilter (CalDAVElement):
     """
@@ -651,7 +658,10 @@
         (caldav_namespace, "text-match"     ): (0, 1),
         (caldav_namespace, "param-filter"   ): (0, None),
     }
-    allowed_attributes = { "name": True }
+    allowed_attributes = {
+        "name": True,
+        "test": False,
+    }
 
 class ParameterFilter (CalDAVElement):
     """
@@ -697,7 +707,8 @@
 
     allowed_attributes = {
         "caseless": False,
-        "negate-condition": False
+        "negate-condition": False,
+        "match-type": False,
     }
 
 class TimeZone (CalDAVTimeZoneElement):

Modified: CalendarServer/trunk/twistedcaldav/query/addressbookquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/addressbookquery.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/twistedcaldav/query/addressbookquery.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -79,10 +79,15 @@
     params = []
     for filter in propfilter.filters:
         if isinstance(filter, addressbookqueryfilter.TextMatch):
-            if filter.negate:
-                params.append(expression.notcontainsExpression(fields[propfilter.filter_name], str(filter.text), True))
-            else:
-                params.append(expression.containsExpression(fields[propfilter.filter_name], str(filter.text), True))
+            if filter.match_type == "equals":
+                tm = expression.isnotExpression if filter.negate else expression.isExpression
+            elif filter.match_type == "contains":
+                tm = expression.notcontainsExpression if filter.negate else expression.containsExpression
+            elif filter.match_type == "starts-with":
+                tm = expression.notstartswithExpression if filter.negate else expression.startswithExpression
+            elif filter.match_type == "ends-with":
+                tm = expression.notendswithExpression if filter.negate else expression.endswithExpression
+            params.append(tm(fields[propfilter.filter_name], str(filter.text), True))
         else:
             # No embedded parameters - not right now as our Index does not handle them
             raise ValueError
@@ -90,9 +95,9 @@
     # Now build return expression
     if len(params) > 1:
         if propfilter.propfilter_test == "anyof":
-            return expression.orExpression[params]
+            return expression.orExpression(params)
         else:
-            return expression.andExpression[params]
+            return expression.andExpression(params)
     elif len(params) == 1:
         return params[0]
     else:

Modified: CalendarServer/trunk/twistedcaldav/query/calendarquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/calendarquery.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/twistedcaldav/query/calendarquery.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2011 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -48,15 +48,18 @@
     assert vcalfilter.filter_name == "VCALENDAR"
     
     if len(vcalfilter.filters) > 0:
+        # Determine logical expression grouping
+        logical = expression.andExpression if vcalfilter.filter_test == "allof" else expression.orExpression
+        
         # Only comp-filters are handled
         for _ignore in [x for x in vcalfilter.filters if not isinstance(x, calendarqueryfilter.ComponentFilter)]:
             raise ValueError
         
-        return compfilterListExpression(vcalfilter.filters, fields)
+        return compfilterListExpression(vcalfilter.filters, fields, logical)
     else:
         return expression.allExpression()
 
-def compfilterListExpression(compfilters, fields):
+def compfilterListExpression(compfilters, fields, logical):
     """
     Create an expression for a list of comp-filter elements.
     
@@ -67,7 +70,7 @@
     if len(compfilters) == 1:
         return compfilterExpression(compfilters[0], fields)
     else:
-        return expression.andExpression([compfilterExpression(c, fields) for c in compfilters])
+        return logical([compfilterExpression(c, fields) for c in compfilters])
 
 def compfilterExpression(compfilter, fields):
     """
@@ -81,7 +84,10 @@
     if not compfilter.defined:
         # Test for TYPE != <<component-type name>>
         return expression.isnotExpression(fields["TYPE"], compfilter.filter_name, True)
-        
+    
+    # Determine logical expression grouping
+    logical = expression.andExpression if compfilter.filter_test == "allof" else expression.orExpression
+    
     expressions = []
     if isinstance(compfilter.filter_name, str):
         expressions.append(expression.isExpression(fields["TYPE"], compfilter.filter_name, True))
@@ -98,7 +104,7 @@
     for p in [x for x in compfilter.filters if isinstance(x, calendarqueryfilter.PropertyFilter)]:
         props.append(propfilterExpression(p, fields))
     if len(props) > 1:
-        propsExpression = expression.andExpression[props]
+        propsExpression = logical(props)
     elif len(props) == 1:
         propsExpression = props[0]
     else:
@@ -109,7 +115,7 @@
     for _ignore in [x for x in compfilter.filters if isinstance(x, calendarqueryfilter.ComponentFilter)]:
         raise ValueError
     if len(comps) > 1:
-        compsExpression = expression.andExpression[comps]
+        compsExpression = logical(comps)
     elif len(comps) == 1:
         compsExpression = comps[0]
     else:
@@ -117,7 +123,7 @@
 
     # Now build compound expression
     if ((propsExpression is not None) and (compsExpression is not None)):
-        expressions.append(expression.andExpression([propsExpression, compsExpression]))
+        expressions.append(logical([propsExpression, compsExpression]))
     elif propsExpression is not None:
         expressions.append(propsExpression)
     elif compsExpression is not None:
@@ -143,6 +149,9 @@
         # Test for <<field>> != "*"
         return expression.isExpression(fields["UID"], "", True)
     
+    # Determine logical expression grouping
+    logical = expression.andExpression if propfilter.filter_test == "allof" else expression.orExpression
+    
     # Handle time-range - we cannot do this with our Index right now
     if propfilter.qualifier and isinstance(propfilter.qualifier, calendarqueryfilter.TimeRange):
         raise ValueError
@@ -150,17 +159,22 @@
     # Handle text-match
     tm = None
     if propfilter.qualifier and isinstance(propfilter.qualifier, calendarqueryfilter.TextMatch):
-        if propfilter.qualifier.negate:
-            tm = expression.notcontainsExpression(fields[propfilter.filter_name], propfilter.qualifier.text, propfilter.qualifier.caseless)
-        else:
-            tm = expression.containsExpression(fields[propfilter.filter_name], propfilter.qualifier.text, propfilter.qualifier.caseless)
+        if propfilter.qualifier.match_type == "equals":
+            tm = expression.isnotExpression if propfilter.qualifier.negate else expression.isExpression
+        elif propfilter.qualifier.match_type == "contains":
+            tm = expression.notcontainsExpression if propfilter.qualifier.negate else expression.containsExpression
+        elif propfilter.qualifier.match_type == "starts-with":
+            tm = expression.notstartswithExpression if propfilter.qualifier.negate else expression.startswithExpression
+        elif propfilter.qualifier.match_type == "ends-with":
+            tm = expression.notendswithExpression if propfilter.qualifier.negate else expression.endswithExpression
+        tm = tm(fields[propfilter.filter_name], propfilter.qualifier.text, propfilter.qualifier.caseless)
     
     # Handle embedded parameters - we do not right now as our Index does not handle them
     params = []
     for _ignore in propfilter.filters:
         raise ValueError
     if len(params) > 1:
-        paramsExpression = expression.andExpression[params]
+        paramsExpression = logical(params)
     elif len(params) == 1:
         paramsExpression = params[0]
     else:
@@ -168,7 +182,7 @@
 
     # Now build return expression
     if (tm is not None) and (paramsExpression is not None):
-        return expression.andExpression([tm, paramsExpression])
+        return logical([tm, paramsExpression])
     elif tm is not None:
         return tm
     elif paramsExpression is not None:

Modified: CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2009-2011 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -184,6 +184,11 @@
             self.filter_name = self.filter_name.encode("utf-8")
         self.defined = not self.qualifier or not isinstance(qualifier, IsNotDefined)
 
+        filter_test = xml_element.attributes.get("test", "allof")
+        if filter_test not in ("anyof", "allof"):
+            raise ValueError("Test must be only one of anyof, allof")
+        self.filter_test = filter_test
+
     def match(self, item, access=None):
         """
         Returns True if the given calendar item (either a component, property or parameter value)
@@ -197,10 +202,11 @@
         if self.qualifier and not self.qualifier.match(item, access): return False
 
         if len(self.filters) > 0:
+            allof = self.filter_test == "allof"
             for filter in self.filters:
-                if not filter._match(item, access):
-                    return False
-            return True
+                if allof != filter._match(item, access):
+                    return not allof
+            return allof
         else:
             return True
 
@@ -224,10 +230,11 @@
         if self.qualifier and not self.qualifier.matchinstance(item, self.instances): return False
 
         if len(self.filters) > 0:
+            allof = self.filter_test == "allof"
             for filter in self.filters:
-                if not filter._match(item, access):
-                    return False
-            return True
+                if allof != filter._match(item, access):
+                    return not allof
+            return allof
         else:
             return True
 
@@ -489,6 +496,18 @@
         else:
             self.negate = False
 
+        if "match-type" in xml_element.attributes:
+            self.match_type = xml_element.attributes["match-type"]
+            if self.match_type not in (
+                "equals",
+                "contains",
+                "starts-with",
+                "ends-with",
+            ):
+                self.match_type = "contains"
+        else:
+            self.match_type = "contains"
+
     def match(self, item, access):
         """
         Match the text for the item.
@@ -508,25 +527,29 @@
 
         def _textCompare(s):
             if self.caseless:
-                if s.lower().find(test) != -1:
-                    return True, not self.negate
+                s = s.lower()
+
+            if self.match_type == "equals":
+                return s == test
+            elif self.match_type == "contains":
+                return s.find(test) != -1 
+            elif self.match_type == "starts-with":
+                return s.startswith(test)
+            elif self.match_type == "ends-with":
+                return s.endswith(test)
             else:
-                if s.find(test) != -1:
-                    return True, not self.negate
-            return False, False
+                return False
 
         for value in values:
             # NB Its possible that we have a text list value which appears as a Python list,
             # so we need to check for that and iterate over the list.
             if isinstance(value, list):
                 for subvalue in value:
-                    matched, result = _textCompare(unicode(subvalue, "utf-8"))
-                    if matched:
-                        return result
+                    if _textCompare(unicode(subvalue, "utf-8")):
+                        return not self.negate
             else:
-                matched, result = _textCompare(unicode(value, "utf-8"))
-                if matched:
-                    return result
+                if _textCompare(unicode(value, "utf-8")):
+                    return not self.negate
         
         return self.negate
 

Modified: CalendarServer/trunk/twistedcaldav/query/expression.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/expression.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/twistedcaldav/query/expression.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -32,6 +32,10 @@
     "notcontainsExpression",
     "isExpression",
     "isnotExpression",
+    "startswithExpression",
+    "notstartswithExpression",
+    "endswithExpression",
+    "notendswithExpression",
     "inExpression",
     "notinExpression",
 ]
@@ -222,7 +226,7 @@
         super(notcontainsExpression, self).__init__(field, text, caseless)
 
     def operator(self):
-        return " does not contain"
+        return "does not contain"
 
 class isExpression(textcompareExpression):
     """
@@ -246,6 +250,50 @@
     def operator(self):
         return "is not"
 
+class startswithExpression(textcompareExpression):
+    """
+    Text STARTSWITH (sub-string match) expression.
+    """
+    
+    def __init__(self, field, text, caseless):
+        super(startswithExpression, self).__init__(field, text, caseless)
+
+    def operator(self):
+        return "starts with"
+
+class notstartswithExpression(textcompareExpression):
+    """
+    Text NOT STARTSWITH (sub-string match) expression.
+    """
+    
+    def __init__(self, field, text, caseless):
+        super(notstartswithExpression, self).__init__(field, text, caseless)
+
+    def operator(self):
+        return "does not start with"
+
+class endswithExpression(textcompareExpression):
+    """
+    Text STARTSWITH (sub-string match) expression.
+    """
+    
+    def __init__(self, field, text, caseless):
+        super(endswithExpression, self).__init__(field, text, caseless)
+
+    def operator(self):
+        return "ends with"
+
+class notendswithExpression(textcompareExpression):
+    """
+    Text NOT STARTSWITH (sub-string match) expression.
+    """
+    
+    def __init__(self, field, text, caseless):
+        super(notendswithExpression, self).__init__(field, text, caseless)
+
+    def operator(self):
+        return "does not end with"
+
 class inExpression(textcompareExpression):
     """
     Text IN (exact string match to one of the supplied items) expression.

Modified: CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2011 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -30,21 +30,25 @@
 
 class sqlgenerator(object):
     
-    FROM           =" from "
-    WHERE          =" where "
-    RESOURCEDB     = "RESOURCE"
-    TIMESPANDB     = "TIMESPAN"
-    TRANSPARENCYDB = "TRANSPARENCY"
-    PERUSERDB      = "PERUSER"
-    NOTOP          = "NOT "
-    ANDOP          = " AND "
-    OROP           = " OR "
-    CONTAINSOP     = " GLOB "
-    NOTCONTAINSOP  = " NOT GLOB "
-    ISOP           = " == "
-    ISNOTOP        = " != "
-    INOP           = " IN "
-    NOTINOP        = " NOT IN "
+    FROM             =" from "
+    WHERE            =" where "
+    RESOURCEDB       = "RESOURCE"
+    TIMESPANDB       = "TIMESPAN"
+    TRANSPARENCYDB   = "TRANSPARENCY"
+    PERUSERDB        = "PERUSER"
+    NOTOP            = "NOT "
+    ANDOP            = " AND "
+    OROP             = " OR "
+    CONTAINSOP       = " GLOB "
+    NOTCONTAINSOP    = " NOT GLOB "
+    ISOP             = " == "
+    ISNOTOP          = " != "
+    STARTSWITHOP     = " GLOB "
+    NOTSTARTSWITHOP  = " NOT GLOB "
+    ENDSWITHOP       = " GLOB "
+    NOTENDSWITHOP    = " NOT GLOB "
+    INOP             = " IN "
+    NOTINOP          = " NOT IN "
 
     FIELDS         = {
         "TYPE": "RESOURCE.TYPE",
@@ -187,6 +191,30 @@
             self.sout.write(self.ISNOTOP)
             self.addArgument(expr.text)
         
+        # STARTSWITH
+        elif isinstance(expr, expression.startswithExpression):
+            self.sout.write(expr.field)
+            self.sout.write(self.STARTSWITHOP)
+            self.addArgument(self.startswithArgument(expr.text))
+        
+        # NOT STARTSWITH
+        elif isinstance(expr, expression.notstartswithExpression):
+            self.sout.write(expr.field)
+            self.sout.write(self.NOTSTARTSWITHOP)
+            self.addArgument(self.startswithArgument(expr.text))
+        
+        # ENDSWITH
+        elif isinstance(expr, expression.endswithExpression):
+            self.sout.write(expr.field)
+            self.sout.write(self.ENDSWITHOP)
+            self.addArgument(self.endswithArgument(expr.text))
+        
+        # NOT ENDSWITH
+        elif isinstance(expr, expression.notendswithExpression):
+            self.sout.write(expr.field)
+            self.sout.write(self.NOTENDSWITHOP)
+            self.addArgument(self.endswithArgument(expr.text))
+        
         # IN
         elif isinstance(expr, expression.inExpression):
             self.sout.write(expr.field)
@@ -259,6 +287,12 @@
     def containsArgument(self, arg):
         return "*%s*" % (arg,)
 
+    def startswithArgument(self, arg):
+        return "%s*" % (arg,)
+
+    def endswithArgument(self, arg):
+        return "*%s" % (arg,)
+
 if __name__ == "__main__":
     
     e1 = expression.isExpression("TYPE", "VEVENT", False)

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -438,9 +438,10 @@
     #
     # Standard (or draft) WebDAV extensions
     #
-    "EnableAddMember"         : True,  # POST ;add-member extension
-    "EnableSyncReport"        : True,  # REPORT collection-sync
-    "EnableWellKnown"         : True,  # /.well-known resource
+    "EnableAddMember"             : True,  # POST ;add-member extension
+    "EnableSyncReport"            : True,  # REPORT collection-sync
+    "EnableWellKnown"             : True,  # /.well-known resource
+    "EnableCalendarQueryExtended" : True,  # Extended calendar-query REPORT
 
     #
     # Non-standard CalDAV extensions
@@ -1202,6 +1203,8 @@
             compliance += customxml.calendarserver_sharing_compliance
             # TODO: This is only needed whilst we do not support scheduling in shared calendars
             compliance += customxml.calendarserver_sharing_no_scheduling_compliance
+        if configDict.EnableCalendarQueryExtended:
+            compliance += caldavxml.caldav_query_extended_compliance
     else:
         compliance = ()
 

Modified: CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2011-06-14 20:52:03 UTC (rev 7594)
+++ CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2011-06-14 20:53:29 UTC (rev 7595)
@@ -860,8 +860,23 @@
     """
 
     ISOP = " = "
-    CONTAINSOP = " LIKE "
-    NOTCONTAINSOP = " NOT LIKE "
+    STARTSWITHOP = ENDSWITHOP = CONTAINSOP = " LIKE "
+    NOTSTARTSWITHOP = NOTENDSWITHOP = NOTCONTAINSOP = " NOT LIKE "
+
+    def containsArgument(self, arg):
+        return "%%%s%%" % (arg,)
+
+    def startswithArgument(self, arg):
+        return "%s%%" % (arg,)
+
+    def endswithArgument(self, arg):
+        return "%%%s" % (arg,)
+
+class CalDAVSQLBehaviorMixin(RealSQLBehaviorMixin):
+    """
+    Query generator for CalDAV indexed searches.
+    """
+
     FIELDS = {
         "TYPE": "CALENDAR_OBJECT.ICALENDAR_TYPE",
         "UID":  "CALENDAR_OBJECT.ICALENDAR_UID",
@@ -967,11 +982,6 @@
         return select, self.arguments
 
 
-    def containsArgument(self, arg):
-        return "%%%s%%" % (arg,)
-
-
-
 class FormatParamStyleMixin(object):
     """
     Mixin for overriding methods on sqlgenerator that generate arguments
@@ -996,7 +1006,7 @@
 
 
 
-class postgresqlgenerator(FormatParamStyleMixin, RealSQLBehaviorMixin,
+class postgresqlgenerator(FormatParamStyleMixin, CalDAVSQLBehaviorMixin,
                           sqlgenerator):
     """
     Query generator for PostgreSQL indexed searches.
@@ -1008,17 +1018,17 @@
 
 
 
-class oraclesqlgenerator(RealSQLBehaviorMixin, sqlgenerator):
+class oraclesqlgenerator(CalDAVSQLBehaviorMixin, sqlgenerator):
     """
     Query generator for Oracle indexed searches.
     """
-    TIMESPANTEST = fixbools(RealSQLBehaviorMixin.TIMESPANTEST)
-    TIMESPANTEST_NOEND = fixbools(RealSQLBehaviorMixin.TIMESPANTEST_NOEND)
-    TIMESPANTEST_NOSTART = fixbools(RealSQLBehaviorMixin.TIMESPANTEST_NOSTART)
+    TIMESPANTEST = fixbools(CalDAVSQLBehaviorMixin.TIMESPANTEST)
+    TIMESPANTEST_NOEND = fixbools(CalDAVSQLBehaviorMixin.TIMESPANTEST_NOEND)
+    TIMESPANTEST_NOSTART = fixbools(CalDAVSQLBehaviorMixin.TIMESPANTEST_NOSTART)
     TIMESPANTEST_TAIL_PIECE = fixbools(
-        RealSQLBehaviorMixin.TIMESPANTEST_TAIL_PIECE)
+        CalDAVSQLBehaviorMixin.TIMESPANTEST_TAIL_PIECE)
     TIMESPANTEST_JOIN_ON_PIECE = fixbools(
-        RealSQLBehaviorMixin.TIMESPANTEST_JOIN_ON_PIECE)
+        CalDAVSQLBehaviorMixin.TIMESPANTEST_JOIN_ON_PIECE)
 
 
 
@@ -1315,23 +1325,16 @@
 
 # CARDDAV
 
-class oraclesqladbkgenerator(sqlgenerator):
+class CardDAVSQLBehaviorMixin(RealSQLBehaviorMixin):
     """
-    Query generator for Oracle indexed searches.
+    Query generator for CardDAV indexed searches.
     """
 
-    ISOP = " = "
-    CONTAINSOP = " LIKE "
-    NOTCONTAINSOP = " NOT LIKE "
     FIELDS = {
         "UID":  "ADDRESSBOOK_OBJECT.VCARD_UID",
     }
     RESOURCEDB = "ADDRESSBOOK_OBJECT"
 
-    def containsArgument(self, arg):
-        return "%%%s%%" % (arg,)
-
-
     def generate(self):
         """
         Generate the actual SQL 'where ...' expression from the passed in
@@ -1368,15 +1371,18 @@
 
 
 
-class postgresqladbkgenerator(FormatParamStyleMixin, oraclesqladbkgenerator):
+class postgresqladbkgenerator(FormatParamStyleMixin, CardDAVSQLBehaviorMixin, sqlgenerator):
     """
-    Query generator for PostgreSQL indexed searches.  Inherit 'real' database
-    behavior from L{oracleadbkgenerator}, and %s-style formatting from
-    L{FormatParamStyleMixin}.
+    Query generator for PostgreSQL indexed searches.
     """
 
+class oraclesqladbkgenerator(CardDAVSQLBehaviorMixin, sqlgenerator):
+    """
+    Query generator for Oracle indexed searches.
+    """
 
 
+
 class PostgresLegacyABIndexEmulator(LegacyIndexHelper):
     """
     Emulator for L{twistedcaldv.index.Index} and
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110614/10e1a543/attachment-0001.html>


More information about the calendarserver-changes mailing list