[CalendarServer-changes] [14559] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Mar 11 11:00:51 PDT 2015


Revision: 14559
          http://trac.calendarserver.org//changeset/14559
Author:   cdaboo at apple.com
Date:     2015-03-11 11:00:51 -0700 (Wed, 11 Mar 2015)
Log Message:
-----------
Make trash collection creation occur only when it is needed. No longer use the name "trash".

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tools/trash.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/common/datastore/podding/migration/test/test_migration.py
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_52_to_53.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_52_to_53.sql
    CalendarServer/trunk/txdav/common/datastore/test/test_trash.py
    CalendarServer/trunk/txdav/common/datastore/test/util.py

Modified: CalendarServer/trunk/calendarserver/tools/trash.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/trash.py	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/calendarserver/tools/trash.py	2015-03-11 18:00:51 UTC (rev 14559)
@@ -91,6 +91,7 @@
     )
 
 
+
 @inlineCallbacks
 def listTrashedCollectionsForPrincipal(service, store, principalUID):
     directory = store.directoryService()
@@ -99,42 +100,48 @@
         print("No record found for:", principalUID)
         returnValue(None)
 
-    txn = store.newTransaction(label="List trashed collections")
-    home = yield txn.calendarHomeWithUID(principalUID)
-    if home is None:
-        print("No home for principal")
-        returnValue(None)
 
-    trash = yield home.childWithName("trash")
+    @inlineCallbacks
+    def doIt(txn):
+        home = yield txn.calendarHomeWithUID(principalUID)
+        if home is None:
+            print("No home for principal")
+            returnValue(None)
 
-    trashedCollections = yield home.children(onlyInTrash=True)
-    if len(trashedCollections) == 0:
-        print("No trashed collections for:", prettyRecord(record))
-        returnValue(None)
+        trash = yield home.getTrash()
+        if trash is None:
+            print("No trash available")
+            returnValue(None)
 
-    print("Listing trashed collections for:", prettyRecord(record))
-    for collection in trashedCollections:
-        displayName = displayNameForCollection(collection)
-        print(
-            "Collection = \"{}\", trashed = {}, id = {}".format(
-                displayName.encode("utf-8"), collection.whenTrashed(),
-                collection._resourceID
+        trashedCollections = yield home.children(onlyInTrash=True)
+        if len(trashedCollections) == 0:
+            print("No trashed collections for:", prettyRecord(record))
+            returnValue(None)
+
+        print("Listing trashed collections for:", prettyRecord(record))
+        for collection in trashedCollections:
+            displayName = displayNameForCollection(collection)
+            print(
+                "Collection = \"{}\", trashed = {}, id = {}".format(
+                    displayName.encode("utf-8"), collection.whenTrashed(),
+                    collection._resourceID
+                )
             )
-        )
-        startTime = collection.whenTrashed() - datetime.timedelta(minutes=5)
-        children = yield trash.trashForCollection(
-            collection._resourceID, start=startTime
-        )
-        print(" ...containing events:")
-        for child in children:
-            component = yield child.component()
-            summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
-            whenTrashed = yield child.whenTrashed()
-            print(" \"{}\", trashed = {}".format(summary.encode("utf-8"), whenTrashed))
+            startTime = collection.whenTrashed() - datetime.timedelta(minutes=5)
+            children = yield trash.trashForCollection(
+                collection._resourceID, start=startTime
+            )
+            print(" ...containing events:")
+            for child in children:
+                component = yield child.component()
+                summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
+                whenTrashed = yield child.whenTrashed()
+                print(" \"{}\", trashed = {}".format(summary.encode("utf-8"), whenTrashed))
 
-    yield txn.commit()
+    yield store.inTransaction(label="List trashed collections", operation=doIt)
 
 
+
 @inlineCallbacks
 def listTrashedEventsForPrincipal(service, store, principalUID):
     directory = store.directoryService()
@@ -143,39 +150,45 @@
         print("No record found for:", principalUID)
         returnValue(None)
 
-    txn = store.newTransaction(label="List trashed collections")
-    home = yield txn.calendarHomeWithUID(principalUID)
-    if home is None:
-        print("No home for principal")
-        returnValue(None)
 
-    trash = yield home.childWithName("trash")
+    @inlineCallbacks
+    def doIt(txn):
+        home = yield txn.calendarHomeWithUID(principalUID)
+        if home is None:
+            print("No home for principal")
+            returnValue(None)
 
-    untrashedCollections = yield home.children(onlyInTrash=False)
-    if len(untrashedCollections) == 0:
-        print("No untrashed collections for:", prettyRecord(record))
-        returnValue(None)
+        trash = yield home.getTrash()
+        if trash is None:
+            print("No trash available")
+            returnValue(None)
 
-    for collection in untrashedCollections:
-        displayName = displayNameForCollection(collection)
-        children = yield trash.trashForCollection(collection._resourceID)
-        if len(children) == 0:
-            continue
+        untrashedCollections = yield home.children(onlyInTrash=False)
+        if len(untrashedCollections) == 0:
+            print("No untrashed collections for:", prettyRecord(record))
+            returnValue(None)
 
-        print("Collection = \"{}\"".format(displayName.encode("utf-8")))
-        for child in children:
-            component = yield child.component()
-            summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
-            whenTrashed = yield child.whenTrashed()
-            print(
-                " \"{}\", trashed = {}, id = {}".format(
-                    summary.encode("utf-8"), whenTrashed, child._resourceID
+        for collection in untrashedCollections:
+            displayName = displayNameForCollection(collection)
+            children = yield trash.trashForCollection(collection._resourceID)
+            if len(children) == 0:
+                continue
+
+            print("Collection = \"{}\"".format(displayName.encode("utf-8")))
+            for child in children:
+                component = yield child.component()
+                summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
+                whenTrashed = yield child.whenTrashed()
+                print(
+                    " \"{}\", trashed = {}, id = {}".format(
+                        summary.encode("utf-8"), whenTrashed, child._resourceID
+                    )
                 )
-            )
 
-    yield txn.commit()
+    yield store.inTransaction(label="List trashed events", operation=doIt)
 
 
+
 @inlineCallbacks
 def restoreTrashedCollection(service, store, principalUID, resourceID):
     directory = store.directoryService()
@@ -184,24 +197,27 @@
         print("No record found for:", principalUID)
         returnValue(None)
 
-    txn = store.newTransaction(label="Restore trashed collection")
-    home = yield txn.calendarHomeWithUID(principalUID)
-    if home is None:
-        print("No home for principal")
-        returnValue(None)
 
-    collection = yield home.childWithID(resourceID, onlyInTrash=True)
-    if collection is None:
-        print("Collection {} is not in the trash".format(resourceID))
-        returnValue(None)
+    @inlineCallbacks
+    def doIt(txn):
+        home = yield txn.calendarHomeWithUID(principalUID)
+        if home is None:
+            print("No home for principal")
+            returnValue(None)
 
-    yield collection.fromTrash(
-        restoreChildren=True, delta=datetime.timedelta(minutes=5), verbose=True
-    )
+        collection = yield home.childWithID(resourceID, onlyInTrash=True)
+        if collection is None:
+            print("Collection {} is not in the trash".format(resourceID))
+            returnValue(None)
 
-    yield txn.commit()
+        yield collection.fromTrash(
+            restoreChildren=True, delta=datetime.timedelta(minutes=5), verbose=True
+        )
 
+    yield store.inTransaction(label="Restore trashed collection", operation=doIt)
 
+
+
 @inlineCallbacks
 def restoreTrashedEvent(service, store, principalUID, resourceID):
     directory = store.directoryService()
@@ -210,27 +226,33 @@
         print("No record found for:", principalUID)
         returnValue(None)
 
-    txn = store.newTransaction(label="Restore trashed collection")
-    home = yield txn.calendarHomeWithUID(principalUID)
-    if home is None:
-        print("No home for principal")
-        returnValue(None)
 
-    trash = yield home.childWithName("trash")
-    child = yield trash.objectResourceWithID(resourceID)
-    if child is None:
-        print("Event not found")
-        returnValue(None)
+    @inlineCallbacks
+    def doIt(txn):
+        home = yield txn.calendarHomeWithUID(principalUID)
+        if home is None:
+            print("No home for principal")
+            returnValue(None)
 
-    component = yield child.component()
-    summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
-    print("Restoring \"{}\"".format(summary.encode("utf-8")))
-    yield child.fromTrash()
+        trash = yield home.getTrash()
+        if trash is None:
+            print("No trash available")
+            returnValue(None)
 
-    yield txn.commit()
+        child = yield trash.objectResourceWithID(resourceID)
+        if child is None:
+            print("Event not found")
+            returnValue(None)
 
+        component = yield child.component()
+        summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
+        print("Restoring \"{}\"".format(summary.encode("utf-8")))
+        yield child.fromTrash()
 
+    yield store.inTransaction(label="Restore trashed event", operation=doIt)
 
+
+
 @inlineCallbacks
 def emptyTrashForPrincipal(service, store, principalUID, days):
     directory = store.directoryService()
@@ -239,45 +261,50 @@
         print("No record found for:", principalUID)
         returnValue(None)
 
-    txn = store.newTransaction(label="List trashed collections")
-    home = yield txn.calendarHomeWithUID(principalUID)
-    if home is None:
-        print("No home for principal")
-        returnValue(None)
 
-    trash = yield home.childWithName("trash")
+    @inlineCallbacks
+    def doIt(txn):
+        home = yield txn.calendarHomeWithUID(principalUID)
+        if home is None:
+            print("No home for principal")
+            returnValue(None)
 
-    untrashedCollections = yield home.children(onlyInTrash=False)
-    if len(untrashedCollections) == 0:
-        print("No untrashed collections for:", prettyRecord(record))
-        returnValue(None)
+        trash = yield home.getTrash()
+        if trash is None:
+            print("No trash available")
+            returnValue(None)
 
-    endTime = datetime.datetime.utcnow() - datetime.timedelta(days=-days)
-    for collection in untrashedCollections:
-        displayName = displayNameForCollection(collection)
-        children = yield trash.trashForCollection(
-            collection._resourceID, end=endTime
-        )
-        if len(children) == 0:
-            continue
+        untrashedCollections = yield home.children(onlyInTrash=False)
+        if len(untrashedCollections) == 0:
+            print("No untrashed collections for:", prettyRecord(record))
+            returnValue(None)
 
-        print("Collection = \"{}\"".format(displayName.encode("utf-8")))
-        for child in children:
-            component = yield child.component()
-            summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
-            whenTrashed = yield child.whenTrashed()
-            print(
-                " \"{}\", trashed = {}, id = {}".format(
-                    summary.encode("utf-8"), whenTrashed, child._resourceID
-                )
+        endTime = datetime.datetime.utcnow() - datetime.timedelta(days=-days)
+        for collection in untrashedCollections:
+            displayName = displayNameForCollection(collection)
+            children = yield trash.trashForCollection(
+                collection._resourceID, end=endTime
             )
-            print("Removing...")
-            yield child.reallyRemove()
+            if len(children) == 0:
+                continue
 
-    yield txn.commit()
+            print("Collection = \"{}\"".format(displayName.encode("utf-8")))
+            for child in children:
+                component = yield child.component()
+                summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
+                whenTrashed = yield child.whenTrashed()
+                print(
+                    " \"{}\", trashed = {}, id = {}".format(
+                        summary.encode("utf-8"), whenTrashed, child._resourceID
+                    )
+                )
+                print("Removing...")
+                yield child.reallyRemove()
 
+    yield store.inTransaction(label="Empty trash", operation=doIt)
 
 
+
 def displayNameForCollection(collection):
     try:
         displayName = collection.properties()[

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2015-03-11 18:00:51 UTC (rev 14559)
@@ -478,6 +478,7 @@
 
         default_collections = tuple([cls._componentDefaultColumn[name] for name in sorted(cls._componentDefaultColumn.keys())])
         return default_collections + (
+            cls._homeMetaDataSchema.TRASH,
             cls._homeMetaDataSchema.ALARM_VEVENT_TIMED,
             cls._homeMetaDataSchema.ALARM_VEVENT_ALLDAY,
             cls._homeMetaDataSchema.ALARM_VTODO_TIMED,
@@ -500,6 +501,7 @@
 
         default_attributes = tuple([cls._componentDefaultAttribute[name] for name in sorted(cls._componentDefaultAttribute.keys())])
         return default_attributes + (
+            "_trash",
             "_alarm_vevent_timed",
             "_alarm_vevent_allday",
             "_alarm_vtodo_timed",
@@ -541,7 +543,7 @@
         values = {}
         for attr, col in zip(self.metadataAttributes(), self.metadataColumns()):
             value = getattr(other, attr)
-            if attr in self._componentDefaultAttribute.values():
+            if attr in self._componentDefaultAttribute.values() + ["_trash", ]:
                 value = calendarIDMap.get(value)
             setattr(self, attr, value)
             values[col] = value

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2015-03-11 18:00:51 UTC (rev 14559)
@@ -467,7 +467,7 @@
         )
         yield migrateHome(fromHome, toHome, lambda x: x.component())
         toCalendars = yield toHome.calendars()
-        self.assertEquals(set([c.name() for c in toCalendars if c.name() not in ("inbox", "trash")]),
+        self.assertEquals(set([c.name() for c in toCalendars if c.name() not in ("inbox",)]),
                           set([k for k in self.requirements['home1'].keys()
                                if self.requirements['home1'][k] is not None]))
         fromCalendars = yield fromHome.calendars()
@@ -499,7 +499,7 @@
         supported_components = set()
         self.assertEqual(len(toCalendars), 2 + len(ical.allowedStoreComponents))
         for calendar in toCalendars:
-            if calendar.name() in ("inbox", "trash"):
+            if calendar.name() in ("inbox",):
                 continue
             result = yield calendar.getSupportedComponents()
             supported_components.add(result)
@@ -527,7 +527,7 @@
         supported_components = set()
         self.assertEqual(len(toCalendars), 3)
         for calendar in toCalendars:
-            if calendar.name() in ("inbox", "trash"):
+            if calendar.name() in ("inbox",):
                 continue
             result = yield calendar.getSupportedComponents()
             supported_components.add(result)
@@ -1950,7 +1950,7 @@
 
         home = yield self.homeUnderTest(name="user01")
         children = yield home.loadChildren()
-        self.assertEqual(len(children), 4)
+        self.assertEqual(len(children), 3)
         yield self.commit()
 
         calendar = yield self.calendarUnderTest(home="user01", name="calendar")

Modified: CalendarServer/trunk/txdav/common/datastore/podding/migration/test/test_migration.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/podding/migration/test/test_migration.py	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/podding/migration/test/test_migration.py	2015-03-11 18:00:51 UTC (rev 14559)
@@ -551,7 +551,7 @@
         calnames = dict([(calendar.name(), calendar) for calendar in calendars])
         self.assertEqual(
             set(calnames.keys()),
-            set(("calendar", "tasks", "inbox", "trash", self.stash["sharename_user02_to_user01"], self.stash["sharename_puser02_to_user01"],))
+            set(("calendar", "tasks", "inbox", self.stash["sharename_user02_to_user01"], self.stash["sharename_puser02_to_user01"],))
         )
 
         # Check shared-by user01 on new pod

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2015-03-11 18:00:51 UTC (rev 14559)
@@ -2320,8 +2320,37 @@
 
 
     @inlineCallbacks
-    def createTrash(self):
-        child = yield self._trashClass.create(self, "trash")
+    def getTrash(self, create=False):
+        child = None
+        if hasattr(self, "_trash"):
+            if self._trash:
+                child = yield self.childWithID(self._trash)
+            elif create:
+                schema = self._homeMetaDataSchema
+
+                # Use a lock to prevent others from creating at the same time
+                yield Select(
+                    From=schema,
+                    Where=(schema.RESOURCE_ID == self.id()),
+                    ForUpdate=True,
+                ).on(self._txn)
+
+                # Re-check to see if someone else created it whilst we were trying to lock
+                self._trash = (yield Select(
+                    [schema.TRASH],
+                    From=schema,
+                    Where=(schema.RESOURCE_ID == self.id()),
+                ).on(self._txn))[0][0]
+                if self._trash:
+                    child = yield self.childWithID(self._trash)
+                else:
+                    child = yield self._trashClass.create(self, str(uuid4()))
+                    self._trash = child.id()
+                    schema = self._homeMetaDataSchema
+                    yield Update(
+                        {schema.TRASH: self._trash},
+                        Where=(schema.RESOURCE_ID == self.id())
+                    ).on(self._txn)
         returnValue(child)
 
 
@@ -3475,7 +3504,6 @@
 
     @inlineCallbacks
     def toTrash(self):
-        # print("XYZZY collection toTrash")
         yield self.ownerDeleteShare()
 
         for resource in (yield self.objectResources()):
@@ -3542,17 +3570,18 @@
         self._home._children[self._home._childrenKey(False)][self._resourceID] = self
 
         if restoreChildren:
-            trash = yield self._home.childWithName("trash")
-            childrenToRestore = yield trash.trashForCollection(
-                self._resourceID, start=startTime
-            )
-            for child in childrenToRestore:
-                if verbose:
-                    component = yield child.component()
-                    summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
-                    print("Recovering \"{}\"".format(summary.encode("utf-8")))
+            trash = yield self._home.getTrash()
+            if trash is not None:
+                childrenToRestore = yield trash.trashForCollection(
+                    self._resourceID, start=startTime
+                )
+                for child in childrenToRestore:
+                    if verbose:
+                        component = yield child.component()
+                        summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
+                        print("Recovering \"{}\"".format(summary.encode("utf-8")))
 
-                yield child.fromTrash()
+                    yield child.fromTrash()
 
 
     @classproperty
@@ -4980,7 +5009,7 @@
     @inlineCallbacks
     def toTrash(self):
         originalCollection = self._parentCollection._resourceID
-        trash = yield self._parentCollection.ownerHome().childWithName("trash")
+        trash = yield self._parentCollection.ownerHome().getTrash(create=True)
         newName = str(uuid4())
         yield self.moveTo(trash, name=newName)
         yield self._updateToTrashQuery.on(

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2015-03-11 18:00:51 UTC (rev 14559)
@@ -52,6 +52,7 @@
 create table CALENDAR_HOME_METADATA (
     "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
     "QUOTA_USED_BYTES" integer default 0 not null,
+    "TRASH" integer default null references CALENDAR on delete set null,
     "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
     "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
     "DEFAULT_POLLS" integer default null references CALENDAR on delete set null,
@@ -654,6 +655,10 @@
 insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
 insert into CALENDARSERVER (NAME, VALUE) values ('NOTIFICATION-DATAVERSION', '1');
 insert into CALENDARSERVER (NAME, VALUE) values ('MIN-VALID-REVISION', '1');
+create index CALENDAR_HOME_METADAT_475de898 on CALENDAR_HOME_METADATA (
+    TRASH
+);
+
 create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
     DEFAULT_EVENTS
 );

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql	2015-03-11 18:00:51 UTC (rev 14559)
@@ -107,6 +107,7 @@
 create table CALENDAR_HOME_METADATA (
   RESOURCE_ID              integer     primary key references CALENDAR_HOME on delete cascade, -- implicit index
   QUOTA_USED_BYTES         integer     default 0 not null,
+  TRASH                    integer     default null references CALENDAR on delete set null,
   DEFAULT_EVENTS           integer     default null references CALENDAR on delete set null,
   DEFAULT_TASKS            integer     default null references CALENDAR on delete set null,
   DEFAULT_POLLS            integer     default null references CALENDAR on delete set null,
@@ -119,6 +120,8 @@
   MODIFIED                 timestamp   default timezone('UTC', CURRENT_TIMESTAMP)
 );
 
+create index CALENDAR_HOME_METADATA_TRASH on
+  CALENDAR_HOME_METADATA(TRASH);
 create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
   CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
 create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_52_to_53.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_52_to_53.sql	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_52_to_53.sql	2015-03-11 18:00:51 UTC (rev 14559)
@@ -19,6 +19,15 @@
 ---------------------------------------------------
 
 -- New columns
+alter table CALENDAR_HOME_METADATA
+  add ("TRASH" integer default null references CALENDAR on delete set null);
+
+create index CALENDAR_HOME_METADAT_475de898 on CALENDAR_HOME_METADATA (
+    TRASH
+);
+
+  
+-- New columns
 alter table CALENDAR_METADATA
   add ("CHILD_TYPE" integer default 0 not null)
   add ("TRASHED" timestamp default null)

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_52_to_53.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_52_to_53.sql	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_52_to_53.sql	2015-03-11 18:00:51 UTC (rev 14559)
@@ -19,6 +19,14 @@
 ---------------------------------------------------
 
 -- New columns
+alter table CALENDAR_HOME_METADATA
+  add column TRASH integer default null references CALENDAR on delete set null;
+
+create index CALENDAR_HOME_METADATA_TRASH on
+  CALENDAR_HOME_METADATA(TRASH);
+
+  
+-- New columns
 alter table CALENDAR_METADATA
   add column CHILD_TYPE     integer      default 0 not null,
   add column TRASHED        timestamp    default null,

Modified: CalendarServer/trunk/txdav/common/datastore/test/test_trash.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/test_trash.py	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/test/test_trash.py	2015-03-11 18:00:51 UTC (rev 14559)
@@ -136,7 +136,7 @@
 
         home = yield txn.calendarHomeWithUID("user01", create=True)
         collection = yield home.childWithName("calendar")
-        trash = yield home.childWithName("trash")
+        trash = yield home.getTrash(create=True)
 
         # No objects
         objects = yield collection.listObjectResources()
@@ -170,7 +170,7 @@
         txn = self.store.newTransaction()
 
         # Verify it's in the trash
-        resource = yield self._getResource(txn, "user01", "trash", newName)
+        resource = yield self._getResource(txn, "user01", trash.name(), newName)
         self.assertTrue((yield resource.isInTrash()))
         trashed = yield resource.whenTrashed()
         self.assertFalse(trashed is None)
@@ -180,7 +180,7 @@
         self.assertEqual(len(resourceNames), 0)
 
         # One object in trash
-        resourceNames = yield self._getResourceNames(txn, "user01", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user01", trash.name())
         self.assertEqual(len(resourceNames), 1)
 
         # Put back from trash
@@ -192,7 +192,7 @@
         txn = self.store.newTransaction()
 
         # Not in trash
-        resource = yield self._getResource(txn, "user01", "trash", "")
+        resource = yield self._getResource(txn, "user01", trash.name(), "")
         self.assertTrue(resource is None)
 
 
@@ -205,7 +205,7 @@
         self.assertTrue(trashed is None)
 
         # No objects in trash
-        resourceNames = yield self._getResourceNames(txn, "user01", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user01", trash.name())
         self.assertEqual(len(resourceNames), 0)
 
         yield txn.commit()
@@ -305,17 +305,19 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user01", "calendar", "test.ics")
         yield resource.remove()
+        home1 = yield self._homeForUser(txn, "user01")
+        trash1 = yield home1.getTrash()
         yield txn.commit()
 
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
 
         # user01's copy is in the trash, still with user02 accepted
         txn = self.store.newTransaction()
-        resource = yield self._getResource(txn, "user01", "trash", "")
+        resource = yield self._getResource(txn, "user01", trash1.name(), "")
         self.assertTrue((yield resource.isInTrash()))
         trashed = yield resource.whenTrashed()
         self.assertFalse(trashed is None)
-        data = yield self._getResourceData(txn, "user01", "trash", "")
+        data = yield self._getResourceData(txn, "user01", trash1.name(), "")
         self.assertTrue("PARTSTAT=ACCEPTED" in data)
         yield txn.commit()
 
@@ -329,14 +331,15 @@
         self.assertTrue("STATUS:CANCELLED" in data)
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
-        data = yield self._getResource(txn, "user02", "trash", "")
-        self.assertEquals(data, None)
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
+        self.assertEquals(trash2, None)
         yield txn.commit()
 
         # user01 restores event from the trash
         txn = self.store.newTransaction()
-        resource = yield self._getResource(txn, "user01", "trash", "")
-        data = yield self._getResourceData(txn, "user01", "trash", "")
+        resource = yield self._getResource(txn, "user01", trash1.name(), "")
+        data = yield self._getResourceData(txn, "user01", trash1.name(), "")
         yield resource.fromTrash()
         yield txn.commit()
 
@@ -491,6 +494,8 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
 
         yield txn.commit()
 
@@ -510,7 +515,7 @@
 
 
         # user02's copy is in the trash only, and still has ACCEPTED
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 1)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -519,7 +524,7 @@
         resourceNames = yield self._getResourceNames(txn, "user02", "inbox")
         self.assertEqual(len(resourceNames), 0)
 
-        data = yield self._getResourceData(txn, "user02", "trash", "")
+        data = yield self._getResourceData(txn, "user02", trash2.name(), "")
         self.assertTrue("PARTSTAT=ACCEPTED" in data)
 
         # result = yield txn.execSQL("select * from calendar_object", [])
@@ -543,7 +548,7 @@
 
         txn = self.store.newTransaction()
 
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 0)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -597,7 +602,7 @@
 
         txn = self.store.newTransaction()
 
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 1)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -742,6 +747,8 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
 
         yield txn.commit()
 
@@ -760,7 +767,7 @@
         yield resource.remove()
 
         # user02's copy is in the trash only, and still has ACCEPTED
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 1)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -769,7 +776,7 @@
         resourceNames = yield self._getResourceNames(txn, "user02", "inbox")
         self.assertEqual(len(resourceNames), 0)
 
-        data = yield self._getResourceData(txn, "user02", "trash", "")
+        data = yield self._getResourceData(txn, "user02", trash2.name(), "")
         self.assertTrue("PARTSTAT=ACCEPTED" in data)
         yield txn.commit()
 
@@ -778,7 +785,7 @@
 
         txn = self.store.newTransaction()
 
-        resource = yield self._getResource(txn, "user02", "trash", "")
+        resource = yield self._getResource(txn, "user02", trash2.name(), "")
         yield resource.reallyRemove()
 
         yield txn.commit()
@@ -794,7 +801,7 @@
 
         txn = self.store.newTransaction()
 
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 0)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -815,7 +822,7 @@
 
         txn = self.store.newTransaction()
 
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 0)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -926,6 +933,8 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
 
         yield txn.commit()
 
@@ -948,7 +957,7 @@
         #     print("ROW", row)
 
         # user02's copy is in the trash only, and still has ACCEPTED
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 1)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -957,14 +966,14 @@
         resourceNames = yield self._getResourceNames(txn, "user02", "inbox")
         self.assertEqual(len(resourceNames), 0)
 
-        data = yield self._getResourceData(txn, "user02", "trash", "")
+        data = yield self._getResourceData(txn, "user02", trash2.name(), "")
         self.assertTrue("PARTSTAT=ACCEPTED" in data)
 
         yield txn.commit()
 
         # user02 moves it from trash
         txn = self.store.newTransaction()
-        resource = yield self._getResource(txn, "user02", "trash", "")
+        resource = yield self._getResource(txn, "user02", trash2.name(), "")
         yield resource.fromTrash()
         yield txn.commit()
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
@@ -982,7 +991,7 @@
         yield resource.remove()
 
         # user02 has nothing in trash
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 0)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -1089,13 +1098,15 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user01", "calendar", "test.ics")
         yield resource.remove()
+        home1 = yield self._homeForUser(txn, "user01")
+        trash1 = yield home1.getTrash()
         yield txn.commit()
 
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
 
         # user01's copy is in the trash, still with user02 partstat
         txn = self.store.newTransaction()
-        data = yield self._getResourceData(txn, "user01", "trash", "")
+        data = yield self._getResourceData(txn, "user01", trash1.name(), "")
         self.assertTrue("PARTSTAT=TENTATIVE" in data)
         yield txn.commit()
 
@@ -1109,14 +1120,15 @@
         self.assertTrue("STATUS:CANCELLED" in data)
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
-        data = yield self._getResource(txn, "user02", "trash", "")
-        self.assertEquals(data, None)
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
+        self.assertEquals(trash2, None)
         yield txn.commit()
 
         # user01 restores event from the trash
         txn = self.store.newTransaction()
-        resource = yield self._getResource(txn, "user01", "trash", "")
-        data = yield self._getResourceData(txn, "user01", "trash", "")
+        resource = yield self._getResource(txn, "user01", trash1.name(), "")
+        data = yield self._getResourceData(txn, "user01", trash1.name(), "")
         yield resource.fromTrash()
         yield txn.commit()
 
@@ -1232,6 +1244,8 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
         yield txn.commit()
 
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
@@ -1249,7 +1263,7 @@
         yield resource.remove()
 
         # user02's copy is in the trash only, and still has TENTATIVE
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 1)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -1258,14 +1272,14 @@
         resourceNames = yield self._getResourceNames(txn, "user02", "inbox")
         self.assertEqual(len(resourceNames), 0)
 
-        data = yield self._getResourceData(txn, "user02", "trash", "")
+        data = yield self._getResourceData(txn, "user02", trash2.name(), "")
         self.assertTrue("PARTSTAT=TENTATIVE" in data)
 
         yield txn.commit()
 
         # user02 moves it from trash
         txn = self.store.newTransaction()
-        resource = yield self._getResource(txn, "user02", "trash", "")
+        resource = yield self._getResource(txn, "user02", trash2.name(), "")
         yield resource.fromTrash()
         yield txn.commit()
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
@@ -1283,7 +1297,7 @@
         yield resource.remove()
 
         # user02 has nothing in trash
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 0)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -1392,13 +1406,15 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user01", "calendar", "test.ics")
         yield resource.remove()
+        home1 = yield self._homeForUser(txn, "user01")
+        trash1 = yield home1.getTrash()
         yield txn.commit()
 
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
 
         # user01's copy is in the trash, still with user02 accepted
         txn = self.store.newTransaction()
-        data = yield self._getResourceData(txn, "user01", "trash", "")
+        data = yield self._getResourceData(txn, "user01", trash1.name(), "")
         self.assertTrue("PARTSTAT=ACCEPTED" in data)
         yield txn.commit()
 
@@ -1412,13 +1428,14 @@
         self.assertTrue("STATUS:CANCELLED" in data)
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
-        data = yield self._getResource(txn, "user02", "trash", "")
-        self.assertEquals(data, None)
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
+        self.assertEquals(trash2, None)
         yield txn.commit()
 
         # user01 restores event from the trash
         txn = self.store.newTransaction()
-        resource = yield self._getResource(txn, "user01", "trash", "")
+        resource = yield self._getResource(txn, "user01", trash1.name(), "")
         trashedName = yield resource.fromTrash()
         yield txn.commit()
 
@@ -1427,7 +1444,7 @@
         txn = self.store.newTransaction()
 
         # user01's trash should be empty
-        resourceNames = yield self._getResourceNames(txn, "user01", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user01", trash1.name())
         self.assertEquals(len(resourceNames), 0)
 
         # user01 should have two .ics
@@ -1451,8 +1468,9 @@
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
         self.assertEquals(len(resourceNames), 1)
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
-        self.assertEquals(len(resourceNames), 0)
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
+        self.assertEquals(trash2, None)
 
         data = yield self._getResourceData(txn, "user02", "calendar", "")
         self.assertTrue("PARTSTAT=NEEDS-ACTION" in data)
@@ -1554,6 +1572,8 @@
         txn = self.store.newTransaction()
         resource = yield self._getResource(txn, "user02", "calendar", "")
         yield resource.remove()
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
         yield txn.commit()
 
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
@@ -1573,7 +1593,7 @@
         yield resource.remove()
 
         # user02's copy is in the trash only, and still has ACCEPTED
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 1)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -1582,13 +1602,13 @@
         resourceNames = yield self._getResourceNames(txn, "user02", "inbox")
         self.assertEqual(len(resourceNames), 0)
 
-        data = yield self._getResourceData(txn, "user02", "trash", "")
+        data = yield self._getResourceData(txn, "user02", trash2.name(), "")
         self.assertTrue("PARTSTAT=ACCEPTED" in data)
 
         yield txn.commit()
         # user02 moves it from trash
         txn = self.store.newTransaction()
-        resource = yield self._getResource(txn, "user02", "trash", "")
+        resource = yield self._getResource(txn, "user02", trash2.name(), "")
         yield resource.fromTrash()
         yield txn.commit()
         yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
@@ -1606,7 +1626,7 @@
         yield resource.remove()
 
         # user02 has nothing in trash
-        resourceNames = yield self._getResourceNames(txn, "user02", "trash")
+        resourceNames = yield self._getResourceNames(txn, "user02", trash2.name())
         self.assertEqual(len(resourceNames), 0)
 
         resourceNames = yield self._getResourceNames(txn, "user02", "calendar")
@@ -1733,7 +1753,9 @@
         self.assertEquals(len(objects), 1)
 
         # No objects in trash
-        trash = yield self._collectionForUser(txn, "user01", "trash")
+        home1 = yield self._homeForUser(txn, "user01")
+        trash1 = yield home1.getTrash(create=True)
+        trash = yield self._collectionForUser(txn, "user01", trash1.name())
         objects = yield trash.listObjectResources()
         self.assertEquals(len(objects), 0)
 
@@ -1755,7 +1777,7 @@
 
         txn = self.store.newTransaction()
         # One object in trash
-        trash = yield self._collectionForUser(txn, "user01", "trash")
+        trash = yield self._collectionForUser(txn, "user01", trash1.name())
         objects = yield trash.listObjectResources()
         self.assertEquals(len(objects), 1)
 
@@ -1837,30 +1859,34 @@
         txn = self.store.newTransaction()
         resource2 = yield self._getResource(txn, "user02", calendarName2, "test.ics")
         yield resource2.remove()
+        home1 = yield self._homeForUser(txn, "user01")
+        trash1 = yield home1.getTrash()
         yield txn.commit()
 
         txn = self.store.newTransaction()
-        names = yield self._getResourceNames(txn, "user01", "trash")
+        names = yield self._getResourceNames(txn, "user01", trash1.name())
         self.assertEquals(len(names), 1)
         names = yield self._getResourceNames(txn, "user01", "calendar")
         self.assertEquals(len(names), 0)
-        names = yield self._getResourceNames(txn, "user02", "trash")
-        self.assertEquals(len(names), 0)
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
+        self.assertEquals(trash2, None)
         names = yield self._getResourceNames(txn, "user02", calendarName2)
         self.assertEquals(len(names), 0)
 
-        resource = yield self._getResource(txn, "user01", "trash", "")
+        resource = yield self._getResource(txn, "user01", trash1.name(), "")
         yield resource.fromTrash()
 
         yield txn.commit()
 
         txn = self.store.newTransaction()
-        names = yield self._getResourceNames(txn, "user01", "trash")
+        names = yield self._getResourceNames(txn, "user01", trash1.name())
         self.assertEquals(len(names), 0)
         names = yield self._getResourceNames(txn, "user01", "calendar")
         self.assertEquals(len(names), 1)
-        names = yield self._getResourceNames(txn, "user02", "trash")
-        self.assertEquals(len(names), 0)
+        home2 = yield self._homeForUser(txn, "user02")
+        trash2 = yield home2.getTrash()
+        self.assertEquals(trash2, None)
         names = yield self._getResourceNames(txn, "user02", calendarName2)
         self.assertEquals(len(names), 1)
 

Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py	2015-03-11 02:18:03 UTC (rev 14558)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py	2015-03-11 18:00:51 UTC (rev 14559)
@@ -483,7 +483,6 @@
                 else:
                     yield home.removeCalendarWithName("calendar")
                 yield home.removeCalendarWithName("inbox")
-                yield home.removeCalendarWithName("trash")
             except NoSuchHomeChildError:
                 pass
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150311/4cab329d/attachment-0001.html>


More information about the calendarserver-changes mailing list