[CalendarServer-changes] [6382] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Sep 28 13:31:07 PDT 2010


Revision: 6382
          http://trac.macosforge.org/projects/calendarserver/changeset/6382
Author:   cdaboo at apple.com
Date:     2010-09-28 13:31:05 -0700 (Tue, 28 Sep 2010)
Log Message:
-----------
Depth:infinity sync support (missing notification collection right now).

Modified Paths:
--------------
    CalendarServer/trunk/twext/web2/dav/resource.py
    CalendarServer/trunk/twistedcaldav/index.py
    CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/storebridge.py
    CalendarServer/trunk/twistedcaldav/test/test_index.py
    CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py
    CalendarServer/trunk/twistedcaldav/vcardindex.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/carddav/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
    CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql
    CalendarServer/trunk/txdav/common/datastore/sql_tables.py
    CalendarServer/trunk/txdav/common/datastore/test/util.py

Modified: CalendarServer/trunk/twext/web2/dav/resource.py
===================================================================
--- CalendarServer/trunk/twext/web2/dav/resource.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twext/web2/dav/resource.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -717,6 +717,12 @@
             returnValue(None)
 
         # First find all depth 1 children
+        names1= []
+        namesDeep = []
+        if names:
+            for name in names:
+                (names1 if name.rstrip("/").find("/") == -1 else namesDeep).append(name.rstrip("/"))
+
         #children = []
         #yield self.findChildren("1", request, lambda x, y: children.append((x, y)), privileges=None, inherited_aces=None)
 
@@ -724,7 +730,7 @@
         basepath = request.urlForResource(self)
         childnames = list(self.listChildren())
         for childname in childnames:
-            if names and childname not in names:
+            if names1 and childname not in names1:
                 continue
             childpath = joinURL(basepath, urllib.quote(childname))
             child = (yield request.locateChildResource(self, childname))
@@ -766,17 +772,24 @@
                     for resource, url in items[2]:
                         badcallback(resource, url)
 
-        # TODO: Depth: infinity support
         if depth == "infinity":
+            # Split names into child collection groups
+            child_collections = {}
+            for name in namesDeep:
+                collection, name = name.split("/", 1)
+                child_collections.setdefault(collection, []).append(name)
+
             for collection, url in allowed_collections:
-                collection_inherited_aces = (
-                    yield collection.inheritedACEsforChildren(request)
-                )
-                yield collection.findChildrenFaster(
-                    depth, request, okcallback, badcallback,
-                    names, privileges,
-                    inherited_aces=collection_inherited_aces
-                )
+                collection_name = collection.name()
+                if collection_name in child_collections:
+                    collection_inherited_aces = (
+                        yield collection.inheritedACEsforChildren(request)
+                    )
+                    yield collection.findChildrenFaster(
+                        depth, request, okcallback, badcallback,
+                        child_collections[collection_name], privileges,
+                        inherited_aces=collection_inherited_aces
+                    )
                 
         returnValue(None)
 

Modified: CalendarServer/trunk/twistedcaldav/index.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/index.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twistedcaldav/index.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -277,7 +277,7 @@
             self.log_info("Search falls outside range of index for %s %s" % (name, minDate))
             self.reExpandResource(name, minDate)
 
-    def whatchanged(self, revision):
+    def whatchanged(self, revision, depth):
 
         results = [(name.encode("utf-8"), deleted) for name, deleted in self._db_execute("select NAME, DELETED from REVISIONS where REVISION > :1", revision)]
         results.sort(key=lambda x:x[1])

Modified: CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -29,11 +29,12 @@
 from twext.web2 import responsecode
 from twext.web2.dav import davxml
 from twext.web2.dav.element.base import WebDAVElement
+from twext.web2.dav.element.extensions import SyncCollection
 from twext.web2.dav.http import MultiStatusResponse, statusForFailure
 from twext.web2.dav.method.prop_common import responseForHref
 from twext.web2.dav.method.propfind import propertyName
 from twext.web2.dav.util import joinURL
-from twext.web2.http import HTTPError
+from twext.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav.config import config
 
@@ -46,16 +47,19 @@
     """
     Generate a sync-collection REPORT.
     """
-    if not config.EnableSyncReport or (
-        not self.isPseudoCalendarCollection() and
-        not self.isAddressBookCollection() and
-        not self.isNotificationCollection()
-    ):
+    
+    # These resource support the report
+    if not config.EnableSyncReport or davxml.Report(SyncCollection(),) not in self.supportedReports():
         log.err("sync-collection report is only allowed on calendar/inbox/addressbook/notification collection resources %s" % (self,))
         raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, davxml.SupportedReport()))
    
     responses = []
 
+    depth = request.headers.getHeader("depth", None)
+    if depth not in ("1", "infinity"):
+        log.err("sync-collection report with invalid depth header: %s" % (depth,))
+        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid Depth header value"))
+        
     propertyreq = sync_collection.property.children if sync_collection.property else None 
     
     @inlineCallbacks
@@ -102,14 +106,14 @@
     # the child resource loop and supply those to the checkPrivileges on each child.
     filteredaces = (yield self.inheritedACEsforChildren(request))
 
-    changed, removed, newtoken = self.whatchanged(sync_collection.sync_token)
+    changed, removed, newtoken = self.whatchanged(sync_collection.sync_token, depth)
 
     # Now determine which valid resources are readable and which are not
     ok_resources = []
     forbidden_resources = []
     if changed:
         yield self.findChildrenFaster(
-            "1",
+            depth,
             request,
             lambda x, y: ok_resources.append((x, y)),
             lambda x, y: forbidden_resources.append((x, y)),

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -371,7 +371,7 @@
                 customxml.PubSubXMPPPushKeyProperty.qname(),
             )
 
-        if config.EnableSyncReport and (self.isPseudoCalendarCollection() or self.isAddressBookCollection()):
+        if config.EnableSyncReport and (davxml.Report(SyncCollection(),) in self.supportedReports()):
             baseProperties += (davxml.SyncToken.qname(),)
             
         if config.EnableAddMember and (self.isCalendarCollection() or self.isAddressBookCollection()):
@@ -540,7 +540,7 @@
             returnValue(customxml.GETCTag.fromString(self.getSyncToken()))
 
         elif qname == davxml.SyncToken.qname() and config.EnableSyncReport and (
-            self.isPseudoCalendarCollection() or self.isAddressBookCollection()
+            davxml.Report(SyncCollection(),) in self.supportedReports()
         ):
             returnValue(davxml.SyncToken.fromString(self.getSyncToken()))
 
@@ -1320,7 +1320,7 @@
 
     # Collection sync stuff
 
-    def whatchanged(self, client_token):
+    def whatchanged(self, client_token, depth):
         
         current_token = self.getSyncToken()
         current_uuid, current_revision = current_token.split("#", 1)
@@ -1342,14 +1342,15 @@
             revision = 0
 
         try:
-            changed, removed = self._indexWhatChanged(revision)
+            changed, removed = self._indexWhatChanged(revision, depth)
         except SyncTokenValidException:
             raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "valid-sync-token")))
 
         return changed, removed, current_token
 
-    def _indexWhatChanged(self, revision):
-        return self.index().whatchanged(revision)
+    def _indexWhatChanged(self, revision, depth):
+        # Now handled directly by newstore
+        raise NotImplementedError
 
     def getSyncToken(self):
         """
@@ -2181,6 +2182,21 @@
         """
         return config.UserQuota if config.UserQuota != 0 else None
 
+    def supportedReports(self):
+        result = super(CommonHomeResource, self).supportedReports()
+        if config.EnableSyncReport:
+            # Allowed on any home
+            result.append(davxml.Report(SyncCollection(),))
+        return result
+
+    def _indexWhatChanged(self, revision, depth):
+        # The newstore implementation supports this directly
+        return self._newStoreHome.resourceNamesSinceToken(revision, depth)
+
+    def getSyncToken(self):
+        # The newstore implementation supports this directly
+        return self._newStoreHome.syncToken()
+
     def canShare(self):
         raise NotImplementedError
 
@@ -2633,6 +2649,9 @@
 # Utilities
 ##
 
+def isCalendarHomeCollectionResource(resource):
+    return isinstance(resource, CalendarHomeResource)
+
 def isCalendarCollectionResource(resource):
     try:
         resource = ICalDAVResource(resource)
@@ -2649,6 +2668,9 @@
     else:
         return resource.isPseudoCalendarCollection()
 
+def isAddressBookHomeCollectionResource(resource):
+    return isinstance(resource, AddressBookHomeResource)
+
 def isAddressBookCollectionResource(resource):
     try:
         resource = ICalDAVResource(resource)

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -241,6 +241,10 @@
         return True
 
 
+    def _indexWhatChanged(self, revision, depth):
+        # The newstore implementation supports this directly
+        return self._newStoreCalendar.resourceNamesSinceToken(revision)
+
     @classmethod
     def transform(cls, self, calendar, home):
         """
@@ -1257,6 +1261,10 @@
         return True
 
 
+    def _indexWhatChanged(self, revision, depth):
+        # The newstore implementation supports this directly
+        return self._newStoreAddressBook.resourceNamesSinceToken(revision)
+
     @classmethod
     def transform(cls, self, addressbook, home):
         """
@@ -1840,7 +1848,7 @@
     def getSyncToken(self):
         return self._newStoreNotifications.syncToken()
 
-    def _indexWhatChanged(self, revision):
+    def _indexWhatChanged(self, revision, depth):
         return self._newStoreNotifications.resourceNamesSinceToken(revision)
 
     def addNotification(self, request, uid, xmltype, xmldata):

Modified: CalendarServer/trunk/twistedcaldav/test/test_index.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_index.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twistedcaldav/test/test_index.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -920,7 +920,8 @@
         )
         
         for revision, results in tests:
-            self.assertEquals(self.db.whatchanged(revision), results, "Mismatched results for whatchanged with revision %d" % (revision,))
+            for depth in ("1", "infinity"):
+                self.assertEquals(self.db.whatchanged(revision, depth), results, "Mismatched results for whatchanged with revision %d" % (revision,))
 
 class MemcacheTests(SQLIndexTests):
     def setUp(self):

Modified: CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -194,7 +194,8 @@
         )
         
         for revision, results in tests:
-            self.assertEquals(self.db.whatchanged(revision), results, "Mismatched results for whatchanged with revision %d" % (revision,))
+            for depth in ("1", "infinity"):
+                self.assertEquals(self.db.whatchanged(revision, depth), results, "Mismatched results for whatchanged with revision %d" % (revision,))
 
 class MemcacheTests(SQLIndexTests):
     def setUp(self):

Modified: CalendarServer/trunk/twistedcaldav/vcardindex.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/vcardindex.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/twistedcaldav/vcardindex.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -385,7 +385,7 @@
         results = self._db_values_for_sql(statement, *names)
         return results
     
-    def whatchanged(self, revision):
+    def whatchanged(self, revision, depth):
 
         results = [(name.encode("utf-8"), deleted) for name, deleted in self._db_execute("select NAME, DELETED from REVISIONS where REVISION > :1", revision)]
         results.sort(key=lambda x:x[1])

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -62,13 +62,15 @@
     implements(ICalendarHome)
 
     def __init__(self, transaction, ownerUID, resourceID, notifier):
-        super(CalendarHome, self).__init__(transaction, ownerUID, resourceID, notifier)
 
-        self._shares = SQLLegacyCalendarShares(self)
         self._childClass = Calendar
         self._childTable = CALENDAR_TABLE
         self._bindTable = CALENDAR_BIND_TABLE
+        self._revisionsTable = CALENDAR_OBJECT_REVISIONS_TABLE
 
+        super(CalendarHome, self).__init__(transaction, ownerUID, resourceID, notifier)
+        self._shares = SQLLegacyCalendarShares(self)
+
     createCalendarWithName = CommonHome.createChildWithName
     removeCalendarWithName = CommonHome.removeChildWithName
     calendarWithName = CommonHome.childWithName

Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -49,13 +49,15 @@
     implements(IAddressBookHome)
 
     def __init__(self, transaction, ownerUID, resourceID, notifier):
-        super(AddressBookHome, self).__init__(transaction, ownerUID, resourceID, notifier)
 
-        self._shares = SQLLegacyAddressBookShares(self)
         self._childClass = AddressBook
         self._childTable = ADDRESSBOOK_TABLE
         self._bindTable = ADDRESSBOOK_BIND_TABLE
+        self._revisionsTable = ADDRESSBOOK_OBJECT_REVISIONS_TABLE
 
+        super(AddressBookHome, self).__init__(transaction, ownerUID, resourceID, notifier)
+        self._shares = SQLLegacyAddressBookShares(self)
+
     addressbooks = CommonHome.children
     listAddressbooks = CommonHome.listChildren
     addressbookWithName = CommonHome.childWithName

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -290,6 +290,8 @@
     _childClass = None
     _childTable = None
     _bindTable = None
+    _revisionsTable = None
+    _notificationRevisionsTable = NOTIFICATION_OBJECT_REVISIONS_TABLE
 
     def __init__(self, transaction, ownerUID, resourceID, notifier):
         self._txn = transaction
@@ -299,6 +301,12 @@
         self._children = {}
         self._notifier = notifier
 
+        # Needed for REVISION/BIND table join
+        self._revisionBindJoinTable = {}
+        for key, value in self._revisionsTable.iteritems():
+            self._revisionBindJoinTable["REV:%s" % (key,)] = value 
+        for key, value in self._bindTable.iteritems():
+            self._revisionBindJoinTable["BIND:%s" % (key,)] = value 
 
     def __repr__(self):
         return "<%s: %s>" % (self.__class__.__name__, self._resourceID)
@@ -432,6 +440,7 @@
         child = self.childWithName(name)
         if not child:
             raise NoSuchHomeChildError()
+        child._deletedSyncToken()
 
         self._txn.execSQL(
             "delete from %(name)s where %(column_RESOURCE_ID)s = %%s" % self._childTable,
@@ -444,6 +453,52 @@
         child.notifyChanged()
 
 
+    def syncToken(self):
+        revision = self._txn.execSQL(
+            """
+            select max(%(column_REVISION)s) from %(name)s
+            where %(column_HOME_RESOURCE_ID)s = %%s
+            """ % self._revisionsTable,
+            [self._resourceID,]
+        )[0][0]
+        return "%s#%s" % (self._resourceID, revision)
+
+    def resourceNamesSinceToken(self, token, depth):
+        results = [
+            (
+                path.encode("utf-8") if path else (collection.encode("utf-8") if collection else ""),
+                name.encode("utf-8") if name else "",
+                deleted
+            )
+            for path, collection, name, deleted in
+            self._txn.execSQL("""
+                select %(BIND:column_RESOURCE_NAME)s, %(REV:column_COLLECTION_NAME)s, %(REV:column_RESOURCE_NAME)s, %(REV:column_DELETED)s
+                from %(REV:name)s
+                left outer join %(BIND:name)s on (%(REV:name)s.%(REV:column_RESOURCE_ID)s = %(BIND:name)s.%(BIND:column_RESOURCE_ID)s)
+                where %(REV:column_REVISION)s > %%s and %(REV:name)s.%(REV:column_HOME_RESOURCE_ID)s = %%s
+                """ % self._revisionBindJoinTable,
+                [token, self._resourceID],
+            )
+        ]
+        
+        deleted = []
+        deleted_collections = set()
+        for path, name, wasdeleted in results:
+            if wasdeleted:
+                if token:
+                    deleted.append("%s/%s" % (path, name,))
+                if not name:
+                    deleted_collections.add(path)
+        
+        changed = []
+        for path, name, wasdeleted in results:
+            if path not in deleted_collections:
+                changed.append("%s/%s" % (path, name,))
+        
+        changed.sort()
+        deleted.sort()
+        return changed, deleted,
+
     @cached
     def properties(self):
         return PropertyStore(
@@ -550,7 +605,7 @@
         # update memos
         del self._home._children[oldName]
         self._home._children[name] = self
-        self._updateSyncToken()
+        self._renameSyncToken()
 
         self.notifyChanged()
 
@@ -654,22 +709,13 @@
         self.notifyChanged()
 
 
-    def _initSyncToken(self):
-        self._txn.execSQL("""
-            insert into %(name)s
-            (%(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
-            values (%%s, %%s, %%s, nextval('%(sequence)s'), FALSE)
-            """ % self._revisionsTable,
-            [self._home._resourceID, self._resourceID, ""]
-        )
-
     def syncToken(self):
         revision = self._txn.execSQL(
             """
             select %(column_REVISION)s from %(name)s
-            where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+            where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
             """ % self._revisionsTable,
-            [self._resourceID, ""]
+            [self._resourceID,]
         )[0][0]
         return "%s#%s" % (self._resourceID, revision,)
 
@@ -678,11 +724,11 @@
 
     def resourceNamesSinceToken(self, token):
         results = [
-            (name.encode("utf-8"), deleted)
+            (name.encode("utf-8") if name else "", deleted)
             for name, deleted in
             self._txn.execSQL("""
                 select %(column_RESOURCE_NAME)s, %(column_DELETED)s from %(name)s
-                where %(column_REVISION)s > %s and %(column_RESOURCE_ID)s = %s
+                where %(column_REVISION)s > %%s and %(column_RESOURCE_ID)s = %%s
                 """ % self._revisionsTable,
                 [token, self._resourceID],
             )
@@ -701,16 +747,65 @@
         
         return changed, deleted,
 
+    def _initSyncToken(self):
+        
+        # Remove any deleted revision entry that uses the same name
+        self._txn.execSQL("""
+            delete from %(name)s
+            where %(column_HOME_RESOURCE_ID)s = %%s and %(column_COLLECTION_NAME)s = %%s
+            """ % self._revisionsTable,
+            [self._home._resourceID, self._name]
+        )
+
+        # Insert new entry
+        self._txn.execSQL("""
+            insert into %(name)s
+            (%(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_ID)s, %(column_COLLECTION_NAME)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
+            values (%%s, %%s, %%s, null, nextval('%(sequence)s'), FALSE)
+            """ % self._revisionsTable,
+            [self._home._resourceID, self._resourceID, self._name]
+        )
+
     def _updateSyncToken(self):
 
         self._txn.execSQL("""
             update %(name)s
             set (%(column_REVISION)s) = (nextval('%(sequence)s'))
-            where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+            where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
             """ % self._revisionsTable,
-            [self._resourceID, ""]
+            [self._resourceID,]
         )
 
+    def _renameSyncToken(self):
+
+        self._txn.execSQL("""
+            update %(name)s
+            set (%(column_REVISION)s, %(column_COLLECTION_NAME)s) = (nextval('%(sequence)s'), %%s)
+            where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
+            """ % self._revisionsTable,
+            [self._name, self._resourceID,]
+        )
+
+    def _deletedSyncToken(self):
+
+        # Remove all child entries
+        self._txn.execSQL("""
+            delete from %(name)s
+            where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_ID)s = %%s and %(column_COLLECTION_NAME)s is null
+            """ % self._revisionsTable,
+            [self._home._resourceID, self._resourceID,]
+        )
+        
+        # Then adjust collection entry to deleted state
+        self._txn.execSQL("""
+            update %(name)s
+            set (%(column_RESOURCE_ID)s, %(column_REVISION)s, %(column_DELETED)s)
+             = (null, nextval('%(sequence)s'), TRUE)
+            where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
+            """ % self._revisionsTable,
+            [self._resourceID,]
+        )
+
     def _insertRevision(self, name):
         self._changeRevision("insert", name)
 
@@ -738,9 +833,9 @@
             self._txn.execSQL("""
                 update %(name)s
                 set (%(column_REVISION)s) = (%%s)
-                where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+                where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
                 """ % self._revisionsTable,
-                [nextrevision, self._resourceID, ""]
+                [nextrevision, self._resourceID,]
             )
         elif action == "update":
             self._txn.execSQL("""
@@ -753,9 +848,9 @@
             self._txn.execSQL("""
                 update %(name)s
                 set (%(column_REVISION)s) = (%%s)
-                where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+                where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
                 """ % self._revisionsTable,
-                [nextrevision, self._resourceID, ""]
+                [nextrevision, self._resourceID,]
             )
         elif action == "insert":
             # Note that an "insert" may happen for a resource that previously existed and then
@@ -788,9 +883,9 @@
             self._txn.execSQL("""
                 update %(name)s
                 set (%(column_REVISION)s) = (%%s)
-                where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+                where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
                 """ % self._revisionsTable,
-                [nextrevision, self._resourceID, ""]
+                [nextrevision, self._resourceID,]
             )
 
     @cached
@@ -1050,18 +1145,18 @@
         self._txn.execSQL("""
             insert into %(name)s
             (%(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
-            values (%%s, %%s, nextval('%(sequence)s'), FALSE)
+            values (%%s, null, nextval('%(sequence)s'), FALSE)
             """ % self._revisionsTable,
-            [self._resourceID, ""]
+            [self._resourceID,]
         )
 
     def syncToken(self):
         revision = self._txn.execSQL(
             """
             select %(column_REVISION)s from %(name)s
-            where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+            where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
             """ % self._revisionsTable,
-            [self._resourceID, ""]
+            [self._resourceID,]
         )[0][0]
         return "%s#%s" % (self._resourceID, revision,)
 
@@ -1078,7 +1173,7 @@
 
     def resourceNamesSinceToken(self, token):
         results = [
-            (name.encode("utf-8"), deleted)
+            (name.encode("utf-8") if name else "", deleted)
             for name, deleted in
             self._txn.execSQL("""
                 select %(column_RESOURCE_NAME)s, %(column_DELETED)s from %(name)s
@@ -1106,9 +1201,9 @@
         self._txn.execSQL("""
             update %(name)s
             set (%(column_REVISION)s) = (nextval('%(sequence)s'))
-            where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+            where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
             """ % self._revisionsTable,
-            [self._resourceID, ""]
+            [self._resourceID,]
         )
 
     def _insertRevision(self, name):
@@ -1138,9 +1233,9 @@
             self._txn.execSQL("""
                 update %(name)s
                 set (%(column_REVISION)s) = (%%s)
-                where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+                where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
                 """ % self._revisionsTable,
-                [nextrevision, self._resourceID, ""]
+                [nextrevision, self._resourceID]
             )
         elif action == "update":
             self._txn.execSQL("""
@@ -1153,9 +1248,9 @@
             self._txn.execSQL("""
                 update %(name)s
                 set (%(column_REVISION)s) = (%%s)
-                where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+                where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
                 """ % self._revisionsTable,
-                [nextrevision, self._resourceID, ""]
+                [nextrevision, self._resourceID]
             )
         elif action == "insert":
             # Note that an "insert" may happen for a resource that previously existed and then
@@ -1188,9 +1283,9 @@
             self._txn.execSQL("""
                 update %(name)s
                 set (%(column_REVISION)s) = (%%s)
-                where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+                where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
                 """ % self._revisionsTable,
-                [nextrevision, self._resourceID, ""]
+                [nextrevision, self._resourceID]
             )
 
     @cached

Modified: CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -906,31 +906,6 @@
             self.log_info("Search falls outside range of index for %s %s" % (name, minDate))
             self.reExpandResource(name, minDate)
 
-    def whatchanged(self, revision):
-
-        results = [
-            (name.encode("utf-8"), deleted)
-            for name, deleted in
-            self._txn.execSQL(
-                """select RESOURCE_NAME, DELETED from CALENDAR_OBJECT_REVISIONS
-                   where REVISION > %s and CALENDAR_RESOURCE_ID = %s""",
-                [revision, self.calendar._resourceID],
-            )
-        ]
-        results.sort(key=lambda x:x[1])
-        
-        changed = []
-        deleted = []
-        for name, wasdeleted in results:
-            if name:
-                if wasdeleted:
-                    if revision:
-                        deleted.append(name)
-                else:
-                    changed.append(name)
-        
-        return changed, deleted,
-
     def indexedSearch(self, filter, useruid='', fbtype=False):
         """
         Finds resources matching the given qualifiers.
@@ -1153,31 +1128,6 @@
         return obj.name()
 
 
-    def whatchanged(self, revision):
-
-        results = [
-            (name.encode("utf-8"), deleted)
-            for name, deleted in
-            self._txn.execSQL(
-                """select RESOURCE_NAME, DELETED from ADDRESSBOOK_OBJECT_REVISIONS
-                   where REVISION > %s and ADDRESSBOOK_RESOURCE_ID = %s""",
-                [revision, self.addressbook._resourceID],
-            )
-        ]
-        results.sort(key=lambda x:x[1])
-        
-        changed = []
-        deleted = []
-        for name, wasdeleted in results:
-            if name:
-                if wasdeleted:
-                    if revision:
-                        deleted.append(name)
-                else:
-                    changed.append(name)
-        
-        return changed, deleted,
-
     def searchValid(self, filter):
         if isinstance(filter, carddavxml.Filter):
             qualifiers = addressbookquery.sqladdressbookquery(filter)

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql	2010-09-28 20:31:05 UTC (rev 6382)
@@ -312,8 +312,9 @@
 
 create table CALENDAR_OBJECT_REVISIONS (
   CALENDAR_HOME_RESOURCE_ID integer      not null references CALENDAR_HOME,
-  CALENDAR_RESOURCE_ID      integer      not null references CALENDAR on delete cascade,
-  RESOURCE_NAME             varchar(255) not null,
+  CALENDAR_RESOURCE_ID      integer      references CALENDAR,
+  CALENDAR_NAME             varchar(255) default null,
+  RESOURCE_NAME             varchar(255),
   REVISION                  integer      not null,
   DELETED                   boolean      not null,
 
@@ -327,8 +328,9 @@
 
 create table ADDRESSBOOK_OBJECT_REVISIONS (
   ADDRESSBOOK_HOME_RESOURCE_ID integer      not null references ADDRESSBOOK_HOME,
-  ADDRESSBOOK_RESOURCE_ID      integer      not null references ADDRESSBOOK on delete cascade,
-  RESOURCE_NAME                varchar(255) not null,
+  ADDRESSBOOK_RESOURCE_ID      integer      references ADDRESSBOOK,
+  ADDRESSBOOK_NAME             varchar(255) default null,
+  RESOURCE_NAME                varchar(255),
   REVISION                     integer      not null,
   DELETED                      boolean      not null,
 
@@ -342,7 +344,7 @@
 
 create table NOTIFICATION_OBJECT_REVISIONS (
   NOTIFICATION_HOME_RESOURCE_ID integer      not null references NOTIFICATION_HOME on delete cascade,
-  RESOURCE_NAME                 varchar(255) not null,
+  RESOURCE_NAME                 varchar(255),
   REVISION                      integer      not null,
   DELETED                       boolean      not null,
 

Modified: CalendarServer/trunk/txdav/common/datastore/sql_tables.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_tables.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/txdav/common/datastore/sql_tables.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -80,6 +80,7 @@
     "sequence"                : "REVISION_SEQ",
     "column_HOME_RESOURCE_ID" : "CALENDAR_HOME_RESOURCE_ID",
     "column_RESOURCE_ID"      : "CALENDAR_RESOURCE_ID",
+    "column_COLLECTION_NAME"  : "CALENDAR_NAME",
     "column_RESOURCE_NAME"    : "RESOURCE_NAME",
     "column_REVISION"         : "REVISION",
     "column_DELETED"          : "DELETED",
@@ -90,6 +91,7 @@
     "sequence"                : "REVISION_SEQ",
     "column_HOME_RESOURCE_ID" : "ADDRESSBOOK_HOME_RESOURCE_ID",
     "column_RESOURCE_ID"      : "ADDRESSBOOK_RESOURCE_ID",
+    "column_COLLECTION_NAME"  : "ADDRESSBOOK_NAME",
     "column_RESOURCE_NAME"    : "RESOURCE_NAME",
     "column_REVISION"         : "REVISION",
     "column_DELETED"          : "DELETED",

Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py	2010-09-28 20:29:02 UTC (rev 6381)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py	2010-09-28 20:31:05 UTC (rev 6382)
@@ -124,6 +124,9 @@
         tables = ['INVITE',
                   'RESOURCE_PROPERTY',
                   'ATTACHMENT',
+                  'NOTIFICATION_OBJECT_REVISIONS',
+                  'ADDRESSBOOK_OBJECT_REVISIONS',
+                  'CALENDAR_OBJECT_REVISIONS',
                   'ADDRESSBOOK_OBJECT',
                   'CALENDAR_OBJECT',
                   'CALENDAR_BIND',
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100928/e9b69c59/attachment-0001.html>


More information about the calendarserver-changes mailing list