[CalendarServer-changes] [6628] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Nov 12 13:22:32 PST 2010


Revision: 6628
          http://trac.macosforge.org/projects/calendarserver/changeset/6628
Author:   cdaboo at apple.com
Date:     2010-11-12 13:22:30 -0800 (Fri, 12 Nov 2010)
Log Message:
-----------
Make depth:1 on a home collection be more efficient wrt SQL use via a load of all child data in one go.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/txdav/base/propertystore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/file.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/common.py
    CalendarServer/trunk/txdav/caldav/icalendarstore.py
    CalendarServer/trunk/txdav/carddav/datastore/file.py
    CalendarServer/trunk/txdav/carddav/datastore/sql.py
    CalendarServer/trunk/txdav/carddav/datastore/test/common.py
    CalendarServer/trunk/txdav/carddav/iaddressbookstore.py
    CalendarServer/trunk/txdav/common/datastore/file.py
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/sql_tables.py

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -2063,6 +2063,24 @@
 
 
     @inlineCallbacks
+    def findChildrenFaster(
+        self, depth, request, okcallback, badcallback,
+        names, privileges, inherited_aces
+    ):
+        """
+        Override to pre-load children in certain collection types for better performance.
+        """
+        
+        if depth == "1":
+            yield self._newStoreHome.loadChildren()
+        
+        result = (yield super(CommonHomeResource, self).findChildrenFaster(
+            depth, request, okcallback, badcallback, names, privileges, inherited_aces
+        ))
+        
+        returnValue(result)
+    
+    @inlineCallbacks
     def makeChild(self, name):
         # Try built-in children first
         if name in self._provisionedChildren:
@@ -2117,8 +2135,8 @@
         """
         children = set(self._provisionedChildren.keys())
         children.update(self._provisionedLinks.keys())
-        children.update((yield self.allShareNames()))
         children.update((yield self._newStoreHome.listChildren()))
+        children.update((yield self._newStoreHome.listSharedChildren()))
         returnValue(children)
 
 

Modified: CalendarServer/trunk/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/sql.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/base/propertystore/sql.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -56,7 +56,35 @@
             self._cached[(name, uid)] = value
         returnValue(self)
 
+    @classmethod
+    @inlineCallbacks
+    def loadAll(cls, defaultuser, txn, joinTable, joinColumn, parentIDColumn, parentID):
+        """
+        Return a list of property stores for all objects in a parent collection
+        """
+        rows = yield txn.execSQL(
+            """
+            select RESOURCE_ID, NAME, VIEWER_UID, VALUE from RESOURCE_PROPERTY
+            left join %s on (RESOURCE_ID = %s) 
+            where %s = %%s
+            """ % (joinTable, joinColumn, parentIDColumn),
+            [parentID]
+        )
+        
+        createdStores = {}
+        for resource_id, name, view_uid, value in rows:
+            if resource_id not in createdStores:
+                store = cls.__new__(cls)
+                super(PropertyStore, store).__init__(defaultuser)
+                store._txn = txn
+                store._resourceID = resource_id
+                store._cached = {}
+                createdStores[resource_id] = store
+            createdStores[resource_id]._cached[(name, view_uid)] = value
 
+        returnValue(createdStores)
+
+
     def _getitem_uid(self, key, uid):
         validKey(key)
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/file.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -124,6 +124,7 @@
             yield name
 
     listCalendars = listChildren
+    loadCalendars = CommonHome.loadChildren
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -82,6 +82,15 @@
         returnValue(wrapped)
 
 
+    @inlineCallbacks
+    def loadCalendars(self):
+        superCalendars = (yield super(ImplicitCalendarHome, self).loadCalendars())
+        wrapped = []
+        for calendar in superCalendars:
+            wrapped.append(ImplicitCalendar(self, calendar))
+        returnValue(wrapped)
+
+
     def createCalendarWithName(self, name):
         self._calendarHome.createCalendarWithName(name)
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -53,7 +53,8 @@
     SQLLegacyCalendarShares, PostgresLegacyInboxIndexEmulator
 from txdav.common.datastore.sql_tables import CALENDAR_TABLE,\
     CALENDAR_BIND_TABLE, CALENDAR_OBJECT_REVISIONS_TABLE, CALENDAR_OBJECT_TABLE,\
-    _ATTACHMENTS_MODE_WRITE, CALENDAR_HOME_TABLE, CALENDAR_HOME_METADATA_TABLE
+    _ATTACHMENTS_MODE_WRITE, CALENDAR_HOME_TABLE, CALENDAR_HOME_METADATA_TABLE,\
+    CALENDAR_AND_CALENDAR_BIND, CALENDAR_OBJECT_REVISIONS_AND_BIND_TABLE
 from txdav.common.icommondatastore import IndexedSearchException
 
 from vobject.icalendar import utc
@@ -84,6 +85,7 @@
     calendarWithName = CommonHome.childWithName
     calendars = CommonHome.children
     listCalendars = CommonHome.listChildren
+    loadCalendars = CommonHome.loadChildren
 
 
     @inlineCallbacks
@@ -116,7 +118,9 @@
 
     _bindTable = CALENDAR_BIND_TABLE
     _homeChildTable = CALENDAR_TABLE
+    _homeChildBindTable = CALENDAR_AND_CALENDAR_BIND
     _revisionsTable = CALENDAR_OBJECT_REVISIONS_TABLE
+    _revisionsBindTable = CALENDAR_OBJECT_REVISIONS_AND_BIND_TABLE
     _objectTable = CALENDAR_OBJECT_TABLE
 
     def __init__(self, home, name, resourceID):

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -774,7 +774,32 @@
             set(home1_calendarNames)
         )
 
+    @inlineCallbacks
+    def test_loadAllCalendars(self):
+        """
+        L{ICalendarHome.loadCalendars} returns an iterable of L{ICalendar}
+        providers, which are consistent with the results from
+        L{ICalendar.calendarWithName}.
+        """
+        # Add a dot directory to make sure we don't find it
+        # self.home1._path.child(".foo").createDirectory()
+        home = yield self.homeUnderTest()
+        calendars = (yield home.loadCalendars())
 
+        for calendar in calendars:
+            self.assertProvides(ICalendar, calendar)
+            self.assertEquals(calendar,
+                              (yield home.calendarWithName(calendar.name())))
+
+        self.assertEquals(
+            set(c.name() for c in calendars),
+            set(home1_calendarNames)
+        )
+        
+        for c in calendars:
+            self.assertTrue(c.properties() is not None)
+
+
     @inlineCallbacks
     def test_calendarsAfterAddCalendar(self):
         """

Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -105,6 +105,13 @@
         @return: an iterable of L{ICalendar}s.
         """
 
+    def loadCalendars():
+        """
+        Pre-load all calendars Depth:1.
+
+        @return: an iterable of L{ICalendar}s.
+        """
+
     def calendarWithName(name):
         """
         Retrieve the calendar with the given C{name} contained in this

Modified: CalendarServer/trunk/txdav/carddav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/file.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/carddav/datastore/file.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -74,6 +74,7 @@
 
     addressbooks = CommonHome.children
     listAddressbooks = CommonHome.listChildren
+    loadAddressbooks = CommonHome.loadChildren
     addressbookWithName = CommonHome.childWithName
     createAddressBookWithName = CommonHome.createChildWithName
     removeAddressBookWithName = CommonHome.removeChildWithName

Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -49,7 +49,8 @@
 from txdav.common.datastore.sql_tables import ADDRESSBOOK_TABLE,\
     ADDRESSBOOK_BIND_TABLE, ADDRESSBOOK_OBJECT_REVISIONS_TABLE,\
     ADDRESSBOOK_OBJECT_TABLE, ADDRESSBOOK_HOME_TABLE,\
-    ADDRESSBOOK_HOME_METADATA_TABLE
+    ADDRESSBOOK_HOME_METADATA_TABLE, ADDRESSBOOK_AND_ADDRESSBOOK_BIND,\
+    ADDRESSBOOK_OBJECT_REVISIONS_AND_BIND_TABLE
 from txdav.base.propertystore.base import PropertyName
 
 
@@ -74,6 +75,7 @@
 
     addressbooks = CommonHome.children
     listAddressbooks = CommonHome.listChildren
+    loadAddressbooks = CommonHome.loadChildren
     addressbookWithName = CommonHome.childWithName
     createAddressBookWithName = CommonHome.createChildWithName
     removeAddressBookWithName = CommonHome.removeChildWithName
@@ -92,7 +94,9 @@
 
     _bindTable = ADDRESSBOOK_BIND_TABLE
     _homeChildTable = ADDRESSBOOK_TABLE
+    _homeChildBindTable = ADDRESSBOOK_AND_ADDRESSBOOK_BIND
     _revisionsTable = ADDRESSBOOK_OBJECT_REVISIONS_TABLE
+    _revisionsBindTable = ADDRESSBOOK_OBJECT_REVISIONS_AND_BIND_TABLE
     _objectTable = ADDRESSBOOK_OBJECT_TABLE
 
     def __init__(self, home, name, resourceID):

Modified: CalendarServer/trunk/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/common.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/common.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -630,6 +630,32 @@
 
 
     @inlineCallbacks
+    def test_loadAllAddressBooks(self):
+        """
+        L{IAddressBookHome.loadAddressBooks} returns an iterable of L{IAddressBook}
+        providers, which are consistent with the results from
+        L{IAddressBook.addressbookWithName}.
+        """
+        # Add a dot directory to make sure we don't find it
+        # self.home1._path.child(".foo").createDirectory()
+        home = yield self.homeUnderTest()
+        addressbooks = (yield home.loadAddressbooks())
+
+        for addressbook in addressbooks:
+            self.assertProvides(IAddressBook, addressbook)
+            self.assertEquals(addressbook,
+                              (yield home.addressbookWithName(addressbook.name())))
+
+        self.assertEquals(
+            set(c.name() for c in addressbooks),
+            set(home1_addressbookNames)
+        )
+        
+        for c in addressbooks:
+            self.assertTrue(c.properties() is not None)
+
+
+    @inlineCallbacks
     def test_addressbooksAfterAddAddressBook(self):
         """
         L{IAddressBookHome.addressbooks} includes addressbooks recently added with

Modified: CalendarServer/trunk/txdav/carddav/iaddressbookstore.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/iaddressbookstore.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/carddav/iaddressbookstore.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -79,6 +79,13 @@
         @return: an iterable of L{IAddressBook}s.
         """
 
+    def loadAddressbooks():
+        """
+        Pre-load all addressbooks Depth:1.
+
+        @return: an iterable of L{IAddressBook}s.
+        """
+
     def addressbookWithName(name):
         """
         Retrieve the addressbook with the given C{name} contained in this

Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/common/datastore/file.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -338,7 +338,11 @@
             if not name.startswith(".")
         )
 
+    # For file store there is no efficient "bulk" load of all children so just
+    # use the "iterate over each child" method.
+    loadChildren = children
 
+
     def listChildren(self):
         """
         Return a set of the names of the child resources.
@@ -352,6 +356,20 @@
         ))
 
 
+    def listSharedChildren(self):
+        """
+        Retrieve the names of the children in this home.
+
+        @return: an iterable of C{str}s.
+        """
+        return [share.localname for share in self._shares.allRecords()]
+
+        if self._childrenLoaded:
+            return succeed(self._sharedChildren.keys())
+        else:
+            return self._childClass.listObjects(self, owned=False)
+
+
     def childWithName(self, name):
         child = self._newChildren.get(name)
         if child is not None:

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -37,7 +37,7 @@
 from twisted.python.modules import getModule
 from twisted.python.util import FancyEqMixin
 
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
 from twisted.application.service import Service
 
@@ -244,9 +244,11 @@
         self._ownerUID = ownerUID
         self._resourceID = None
         self._shares = None
+        self._childrenLoaded = False
         self._children = {}
         self._sharedChildren = {}
         self._notifier = notifier
+        self._quotaUsedBytes = None
 
         # Needed for REVISION/BIND table join
         self._revisionBindJoinTable = {}
@@ -360,13 +362,32 @@
         returnValue(x)
 
 
+    @inlineCallbacks
+    def loadChildren(self):
+        """
+        Load and cache all children - Depth:1 optimization
+        """
+        results1 = (yield self._childClass.loadAllChildren(self, owned=True))
+        for result in results1:
+            self._children[result.name()] = result
+        results2 = (yield self._childClass.loadAllChildren(self, owned=False))
+        for result in results2:
+            self._sharedChildren[result.name()] = result
+        self._childrenLoaded = True
+        returnValue(results1 + results2)
+
+
     def listChildren(self):
         """
         Retrieve the names of the children in this home.
 
         @return: an iterable of C{str}s.
         """
-        return self._childClass.listObjects(self, owned=True)
+        
+        if self._childrenLoaded:
+            return succeed(self._children.keys())
+        else:
+            return self._childClass.listObjects(self, owned=True)
 
 
     def listSharedChildren(self):
@@ -375,7 +396,10 @@
 
         @return: an iterable of C{str}s.
         """
-        return self._childClass.listObjects(self, owned=False)
+        if self._childrenLoaded:
+            return succeed(self._sharedChildren.keys())
+        else:
+            return self._childClass.listObjects(self, owned=False)
 
 
     @memoizedKey("name", "_children")
@@ -581,11 +605,15 @@
 
     @inlineCallbacks
     def quotaUsedBytes(self):
-        returnValue((yield self._txn.execSQL(
-            "select %(column_QUOTA_USED_BYTES)s from %(name)s"
-            " where %(column_RESOURCE_ID)s = %%s" % self._homeMetaDataTable,
-            [self._resourceID]
-        ))[0][0])
+        
+        if self._quotaUsedBytes is None:
+            self._quotaUsedBytes = (yield self._txn.execSQL(
+                "select %(column_QUOTA_USED_BYTES)s from %(name)s"
+                " where %(column_RESOURCE_ID)s = %%s" % self._homeMetaDataTable,
+                [self._resourceID]
+            ))[0][0]
+        
+        returnValue(self._quotaUsedBytes)
 
 
     @inlineCallbacks
@@ -603,7 +631,7 @@
             [self._resourceID]
         )
 
-        quotaUsedBytes = (yield self._txn.execSQL("""
+        self._quotaUsedBytes = (yield self._txn.execSQL("""
             update %(name)s
             set %(column_QUOTA_USED_BYTES)s = %(column_QUOTA_USED_BYTES)s + %%s
             where %(column_RESOURCE_ID)s = %%s
@@ -611,9 +639,10 @@
             """ % self._homeMetaDataTable,
             [delta, self._resourceID]
         ))[0][0]
+
         # Double check integrity
-        if quotaUsedBytes < 0:
-            log.error("Fixing quota adjusted below zero to %s by change amount %s" % (quotaUsedBytes, delta,))
+        if self._quotaUsedBytes < 0:
+            log.error("Fixing quota adjusted below zero to %s by change amount %s" % (self._quotaUsedBytes, delta,))
             yield self._txn.execSQL("""
                 update %(name)s
                 set %(column_QUOTA_USED_BYTES)s = 0
@@ -621,6 +650,7 @@
                 """ % self._homeMetaDataTable,
                 [self._resourceID]
             )
+            self._quotaUsedBytes = 0
 
 
     def notifierID(self, label="default"):
@@ -647,7 +677,9 @@
     _objectResourceClass = None
     _bindTable = None
     _homeChildTable = None
+    _homeChildBindTable = None
     _revisionsTable = None
+    _revisionsBindTable = None
     _objectTable = None
 
     def __init__(self, home, name, resourceID):
@@ -657,6 +689,7 @@
         self._created = None
         self._modified = None
         self._objects = {}
+        self._syncTokenRevision = None
 
         if home._notifier:
             childID = "%s/%s" % (home.uid(), name)
@@ -703,6 +736,70 @@
 
     @classmethod
     @inlineCallbacks
+    def loadAllChildren(cls, home, owned):
+        """
+        Load all child objects and return a list of them. This must create the child classes
+        and initialize them using "batched" SQL operations to keep this contacts wrt the number of
+        children. This is an optimization for Depth:1 operations on the home.
+        """
+        
+        results = []
+
+        # Load from the main table first
+        dataRows = (yield home._txn.execSQL(("""
+            select %(CHILD:column_RESOURCE_ID)s, %(BIND:column_RESOURCE_NAME)s, %(CHILD:column_CREATED)s, %(CHILD:column_MODIFIED)s
+            from %(CHILD:name)s
+            left outer join %(BIND:name)s on (%(CHILD:column_RESOURCE_ID)s = %(BIND:column_RESOURCE_ID)s)
+            where
+              %(BIND:column_HOME_RESOURCE_ID)s = %%s and
+              %(BIND:column_BIND_MODE)s """ + ("=" if owned else "!=") + """ %%s
+            """) % cls._homeChildBindTable,
+            [
+                home._resourceID,
+                _BIND_MODE_OWN,
+            ]
+        ))
+        
+        if dataRows:
+            # Get property stores for all these child resources (if any found)
+            propertyStores =(yield PropertyStore.loadAll(
+                home.uid(),
+                home._txn,
+                cls._bindTable["name"],
+                cls._bindTable["column_RESOURCE_ID"],
+                cls._bindTable["column_HOME_RESOURCE_ID"],
+                home._resourceID,
+            ))
+
+            revisions = (yield home._txn.execSQL(("""
+                select %(REV:name)s.%(REV:column_RESOURCE_ID)s, max(%(REV:column_REVISION)s) from %(REV:name)s
+                left join %(BIND:name)s on (%(REV:name)s.%(REV:column_RESOURCE_ID)s = %(BIND:name)s.%(BIND:column_RESOURCE_ID)s)
+                where
+                  %(BIND:name)s.%(BIND:column_HOME_RESOURCE_ID)s = %%s and
+                  %(BIND:column_BIND_MODE)s """ + ("=" if owned else "!=") + """ %%s and
+                  %(REV:column_DELETED)s = FALSE
+                group by %(REV:name)s.%(REV:column_RESOURCE_ID)s
+                """) % cls._revisionsBindTable,
+                [
+                    home._resourceID,
+                    _BIND_MODE_OWN,
+                ]
+            ))
+            revisions = dict(revisions)
+        
+        # Create the actual objects merging in properties
+        for resource_id, resource_name, created, modified in dataRows:
+            child = cls(home, resource_name, resource_id)
+            child._created = created
+            child._modified = modified
+            child._syncTokenRevision = revisions[resource_id]
+            child._loadPropertyStore(propertyStores.get(resource_id, None))
+            results.append(child)
+        
+        returnValue(results)
+
+    @classmethod
+    @inlineCallbacks
     def objectWithName(cls, home, name, owned):
         """
         Retrieve the child with the given C{name} contained in this
@@ -1036,22 +1133,15 @@
 
     @inlineCallbacks
     def syncToken(self):
-        revision = (yield self._txn.execSQL(
-            """
-            select max(%(column_REVISION)s) from %(name)s
-            where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is not null
-            """ % self._revisionsTable,
-            [self._resourceID,]
-        ))[0][0]
-        if revision is None:
-            revision = (yield self._txn.execSQL(
+        if self._syncTokenRevision is None:
+            self._syncTokenRevision = (yield self._txn.execSQL(
                 """
-                select %(column_REVISION)s from %(name)s
-                where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
+                select max(%(column_REVISION)s) from %(name)s
+                where %(column_RESOURCE_ID)s = %%s
                 """ % self._revisionsTable,
                 [self._resourceID,]
             ))[0][0]
-        returnValue(("%s#%s" % (self._resourceID, revision,)))
+        returnValue(("%s#%s" % (self._resourceID, self._syncTokenRevision,)))
 
 
     def objectResourcesSinceToken(self, token):
@@ -1097,37 +1187,40 @@
         )
 
         # Insert new entry
-        yield self._txn.execSQL("""
+        self._syncTokenRevision = (yield 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)
+            returning %(column_REVISION)s
             """ % self._revisionsTable,
             [self._home._resourceID, self._resourceID, self._name]
-        )
+        ))[0][0]
 
 
     @inlineCallbacks
     def _updateSyncToken(self):
 
-        yield self._txn.execSQL("""
+        self._syncTokenRevision = (yield self._txn.execSQL("""
             update %(name)s
             set (%(column_REVISION)s) = (nextval('%(sequence)s'))
             where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
+            returning %(column_REVISION)s
             """ % self._revisionsTable,
             [self._resourceID,]
-        )
+        ))[0][0]
 
 
     @inlineCallbacks
     def _renameSyncToken(self):
 
-        yield self._txn.execSQL("""
+        self._syncTokenRevision = (yield 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
+            returning %(column_REVISION)s
             """ % self._revisionsTable,
             [self._name, self._resourceID,]
-        )
+        ))[0][0]
 
 
     @inlineCallbacks
@@ -1142,15 +1235,17 @@
         )
 
         # Then adjust collection entry to deleted state (do this for all entries with this collection's
-        # resource-id so that we deal with direct shares which are not normally removed thorugh an unshare
+        # resource-id so that we deal with direct shares which are not normally removed through an unshare
         yield 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
+            returning %(column_REVISION)s
             """ % self._revisionsTable,
             [self._resourceID,]
         )
+        self._syncTokenRevision = None
 
 
     def _insertRevision(self, name):
@@ -1214,15 +1309,18 @@
                     """ % self._revisionsTable,
                     [self._home._resourceID, self._resourceID, name, nextrevision]
                 )
+        
+        self._syncTokenRevision = nextrevision
 
 
     @inlineCallbacks
-    def _loadPropertyStore(self):
-        props = yield PropertyStore.load(
-            self.ownerHome().uid(),
-            self._txn,
-            self._resourceID
-        )
+    def _loadPropertyStore(self, props=None):
+        if props is None:
+            props = yield PropertyStore.load(
+                self.ownerHome().uid(),
+                self._txn,
+                self._resourceID
+            )
         self.initPropertyStore(props)
         self._properties = props
 

Modified: CalendarServer/trunk/txdav/common/datastore/sql_tables.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_tables.py	2010-11-12 19:03:22 UTC (rev 6627)
+++ CalendarServer/trunk/txdav/common/datastore/sql_tables.py	2010-11-12 21:22:30 UTC (rev 6628)
@@ -161,3 +161,20 @@
 _BIND_MODE_READ = 1
 _BIND_MODE_WRITE = 2
 _BIND_MODE_DIRECT = 3
+
+# Some combined tables used in joins
+CALENDAR_AND_CALENDAR_BIND = {}
+CALENDAR_AND_CALENDAR_BIND.update([("CHILD:%s" % (k,), v) for k,v in CALENDAR_TABLE.items()])
+CALENDAR_AND_CALENDAR_BIND.update([("BIND:%s" % (k,), v) for k,v in CALENDAR_BIND_TABLE.items()])
+
+CALENDAR_OBJECT_REVISIONS_AND_BIND_TABLE = {}
+CALENDAR_OBJECT_REVISIONS_AND_BIND_TABLE.update([("REV:%s" % (k,), v) for k,v in CALENDAR_OBJECT_REVISIONS_TABLE.items()])
+CALENDAR_OBJECT_REVISIONS_AND_BIND_TABLE.update([("BIND:%s" % (k,), v) for k,v in CALENDAR_BIND_TABLE.items()])
+
+ADDRESSBOOK_AND_ADDRESSBOOK_BIND = {}
+ADDRESSBOOK_AND_ADDRESSBOOK_BIND.update([("CHILD:%s" % (k,), v) for k,v in ADDRESSBOOK_TABLE.items()])
+ADDRESSBOOK_AND_ADDRESSBOOK_BIND.update([("BIND:%s" % (k,), v) for k,v in ADDRESSBOOK_BIND_TABLE.items()])
+
+ADDRESSBOOK_OBJECT_REVISIONS_AND_BIND_TABLE = {}
+ADDRESSBOOK_OBJECT_REVISIONS_AND_BIND_TABLE.update([("REV:%s" % (k,), v) for k,v in ADDRESSBOOK_OBJECT_REVISIONS_TABLE.items()])
+ADDRESSBOOK_OBJECT_REVISIONS_AND_BIND_TABLE.update([("BIND:%s" % (k,), v) for k,v in ADDRESSBOOK_BIND_TABLE.items()])
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20101112/636e2571/attachment-0001.html>


More information about the calendarserver-changes mailing list