[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