[CalendarServer-changes] [7184] CalendarServer/trunk/txdav
source_changes at macosforge.org
source_changes at macosforge.org
Fri Mar 11 11:38:45 PST 2011
Revision: 7184
http://trac.macosforge.org/projects/calendarserver/changeset/7184
Author: cdaboo at apple.com
Date: 2011-03-11 11:38:43 -0800 (Fri, 11 Mar 2011)
Log Message:
-----------
Make sure properties in SQL table are removed when the corresponding resource is removed.
Modified Paths:
--------------
CalendarServer/trunk/txdav/base/propertystore/base.py
CalendarServer/trunk/txdav/base/propertystore/none.py
CalendarServer/trunk/txdav/base/propertystore/sql.py
CalendarServer/trunk/txdav/base/propertystore/xattr.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
Modified: CalendarServer/trunk/txdav/base/propertystore/base.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/base.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/base/propertystore/base.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -147,7 +147,10 @@
def _keys_uid(self, uid):
raise NotImplementedError()
-
+
+ def _removeResource(self):
+ raise NotImplementedError()
+
def flush(self):
raise NotImplementedError()
Modified: CalendarServer/trunk/txdav/base/propertystore/none.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/none.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/base/propertystore/none.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -50,6 +50,9 @@
def keys(self):
return ()
+ def _removeResource(self):
+ pass
+
#
# I/O
#
Modified: CalendarServer/trunk/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/sql.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/base/propertystore/sql.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -204,4 +204,13 @@
if cachedUID == uid:
yield PropertyName.fromString(cachedKey)
+ _deleteResourceQuery = Delete(
+ prop, Where=(prop.RESOURCE_ID == Parameter("resourceID"))
+ )
+ def _removeResource(self):
+
+ self._cached = {}
+ self._deleteResourceQuery.on(self._txn, resourceID=self._resourceID)
+ self._cacher.delete(str(self._resourceID))
+
Modified: CalendarServer/trunk/txdav/base/propertystore/xattr.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/xattr.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/base/propertystore/xattr.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -257,6 +257,11 @@
if effectivekey[1] == uid and effectivekey not in seen:
yield effectivekey[0]
+ def _removeResource(self):
+ # xattrs are removed when the underlying file is deleted so just clear out cached changes
+ self.removed.clear()
+ self.modified.clear()
+
#
# I/O
#
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -13,34 +13,37 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-import datetime
-from twistedcaldav.dateops import datetimeMktime
"""
Tests for txdav.caldav.datastore.postgres, mostly based on
L{txdav.caldav.datastore.test.common}.
"""
-from twisted.trial import unittest
+from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.task import deferLater
-from twisted.internet import reactor
from twisted.python import hashlib
+from twisted.trial import unittest
+from twext.enterprise.dal.syntax import Select, Parameter
from twext.python.vcomponent import VComponent
from twext.web2.dav.element.rfc2518 import GETContentLanguage, ResourceType
-from txdav.caldav.datastore.test.common import CommonTests as CalendarCommonTests
+from txdav.base.propertystore.base import PropertyName
+from txdav.caldav.datastore.test.common import CommonTests as CalendarCommonTests,\
+ event4_text
+from txdav.caldav.datastore.test.test_file import setUpCalendarStore
+from txdav.caldav.datastore.util import _migrateCalendar, migrateHome
from txdav.common.datastore.sql import ECALENDARTYPE
+from txdav.common.datastore.sql_tables import schema
from txdav.common.datastore.test.util import buildStore, populateCalendarsFrom
-from txdav.caldav.datastore.test.test_file import setUpCalendarStore
-from txdav.caldav.datastore.util import _migrateCalendar, migrateHome
-from txdav.base.propertystore.base import PropertyName
-
-from twistedcaldav import memcacher
+from twistedcaldav import memcacher, caldavxml
from twistedcaldav.config import config
+from twistedcaldav.dateops import datetimeMktime
+import datetime
+
class CalendarSQLStorageTests(CalendarCommonTests, unittest.TestCase):
"""
Calendar SQL storage tests.
@@ -433,5 +436,140 @@
self.assertNotEqual(notification_uid1_1, None)
self.assertNotEqual(notification_uid1_2, None)
+ @inlineCallbacks
+ def test_removeCalendarPropertiesOnDelete(self):
+ """
+ L{ICalendarHome.removeCalendarWithName} removes a calendar that already
+ exists and makes sure properties are also removed.
+ """
-
\ No newline at end of file
+ # Create calendar and add a property
+ home = yield self.homeUnderTest()
+ name = "remove-me"
+ calendar = yield home.createCalendarWithName(name)
+ resourceID = calendar._resourceID
+ calendarProperties = calendar.properties()
+
+ prop = caldavxml.CalendarDescription.fromString("Calendar to be removed")
+ calendarProperties[PropertyName.fromElement(prop)] = prop
+ yield self.commit()
+
+ prop = schema.RESOURCE_PROPERTY
+ _allWithID = Select([prop.NAME, prop.VIEWER_UID, prop.VALUE],
+ From=prop,
+ Where=prop.RESOURCE_ID == Parameter("resourceID"))
+
+ # Check that two properties are present
+ home = yield self.homeUnderTest()
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 2)
+ yield self.commit()
+
+ # Remove calendar and check for no properties
+ home = yield self.homeUnderTest()
+ yield home.removeCalendarWithName(name)
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ # Recheck it
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ @inlineCallbacks
+ def test_removeCalendarObjectPropertiesOnDelete(self):
+ """
+ L{ICalendarHome.removeCalendarWithName} removes a calendar object that already
+ exists and makes sure properties are also removed (which is always the case as right
+ now calendar objects never have properties).
+ """
+
+ # Create calendar object
+ calendar1 = yield self.calendarUnderTest()
+ name = "4.ics"
+ component = VComponent.fromString(event4_text)
+ metadata = {
+ "accessMode": "PUBLIC",
+ "isScheduleObject": True,
+ "scheduleTag": "abc",
+ "scheduleEtags": (),
+ "hasPrivateComment": False,
+ }
+ calobject = yield calendar1.createCalendarObjectWithName(name, component, metadata=metadata)
+ resourceID = calobject._resourceID
+
+ prop = schema.RESOURCE_PROPERTY
+ _allWithID = Select([prop.NAME, prop.VIEWER_UID, prop.VALUE],
+ From=prop,
+ Where=prop.RESOURCE_ID == Parameter("resourceID"))
+
+ # No properties on existing calendar object
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+
+ yield self.commit()
+
+ # Remove calendar and check for no properties
+ calendar1 = yield self.calendarUnderTest()
+ yield calendar1.removeCalendarObjectWithName(name)
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ # Recheck it
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ @inlineCallbacks
+ def test_removeInboxObjectPropertiesOnDelete(self):
+ """
+ L{ICalendarHome.removeCalendarWithName} removes an inbox calendar object that already
+ exists and makes sure properties are also removed. Inbox calendar objects can have properties.
+ """
+
+ # Create calendar object and add a property
+ home = yield self.homeUnderTest()
+ inbox = yield home.createCalendarWithName("inbox")
+
+ name = "4.ics"
+ component = VComponent.fromString(event4_text)
+ metadata = {
+ "accessMode": "PUBLIC",
+ "isScheduleObject": True,
+ "scheduleTag": "abc",
+ "scheduleEtags": (),
+ "hasPrivateComment": False,
+ }
+ calobject = yield inbox.createCalendarObjectWithName(name, component, metadata=metadata)
+ resourceID = calobject._resourceID
+ calobjectProperties = calobject.properties()
+
+ prop = caldavxml.CalendarDescription.fromString("Calendar object to be removed")
+ calobjectProperties[PropertyName.fromElement(prop)] = prop
+ yield self.commit()
+
+ prop = schema.RESOURCE_PROPERTY
+ _allWithID = Select([prop.NAME, prop.VIEWER_UID, prop.VALUE],
+ From=prop,
+ Where=prop.RESOURCE_ID == Parameter("resourceID"))
+
+ # One property exists calendar object
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 1)
+
+ yield self.commit()
+
+ # Remove calendar object and check for no properties
+ home = yield self.homeUnderTest()
+ inbox = yield home.calendarWithName("inbox")
+ yield inbox.removeCalendarObjectWithName(name)
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ # Recheck it
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
Modified: CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -19,23 +19,27 @@
L{txdav.carddav.datastore.test.common}.
"""
-from txdav.carddav.datastore.test.common import CommonTests as AddressBookCommonTests
-
-from txdav.common.datastore.sql import EADDRESSBOOKTYPE
-from txdav.common.datastore.test.util import buildStore
-from txdav.carddav.datastore.test.test_file import setUpAddressBookStore
+from twext.enterprise.dal.syntax import Select, Parameter
from twext.web2.dav.element.rfc2518 import GETContentLanguage, ResourceType
-from txdav.base.propertystore.base import PropertyName
-from txdav.carddav.datastore.util import _migrateAddressbook, migrateHome
+from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.trial import unittest
-from twisted.internet.defer import inlineCallbacks, returnValue
-from twistedcaldav import memcacher
+from twistedcaldav import memcacher, carddavxml
from twistedcaldav.config import config
from twistedcaldav.vcard import Component as VCard
+from twistedcaldav.vcard import Component as VComponent
+from txdav.base.propertystore.base import PropertyName
+from txdav.carddav.datastore.test.common import CommonTests as AddressBookCommonTests,\
+ vcard4_text
+from txdav.carddav.datastore.test.test_file import setUpAddressBookStore
+from txdav.carddav.datastore.util import _migrateAddressbook, migrateHome
+from txdav.common.datastore.sql import EADDRESSBOOKTYPE
+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.test.util import buildStore
+
class AddressBookSQLStorageTests(AddressBookCommonTests, unittest.TestCase):
"""
AddressBook SQL storage tests.
@@ -260,3 +264,83 @@
yield d1
yield d2
+
+ @inlineCallbacks
+ def test_removeAddressBookPropertiesOnDelete(self):
+ """
+ L{IAddressBookHome.removeAddressBookWithName} removes an address book that already
+ exists and makes sure properties are also removed.
+ """
+
+ # Create address book and add a property
+ home = yield self.homeUnderTest()
+ name = "remove-me"
+ addressbook = yield home.createAddressBookWithName(name)
+ resourceID = addressbook._resourceID
+ addressbookProperties = addressbook.properties()
+
+ prop = carddavxml.AddressBookDescription.fromString("Address Book to be removed")
+ addressbookProperties[PropertyName.fromElement(prop)] = prop
+ yield self.commit()
+
+ prop = schema.RESOURCE_PROPERTY
+ _allWithID = Select([prop.NAME, prop.VIEWER_UID, prop.VALUE],
+ From=prop,
+ Where=prop.RESOURCE_ID == Parameter("resourceID"))
+
+ # Check that two properties are present
+ home = yield self.homeUnderTest()
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 2)
+ yield self.commit()
+
+ # Remove address book and check for no properties
+ home = yield self.homeUnderTest()
+ yield home.removeAddressBookWithName(name)
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ # Recheck it
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ @inlineCallbacks
+ def test_removeAddressBookObjectPropertiesOnDelete(self):
+ """
+ L{IAddressBookHome.removeAddressBookWithName} removes an address book object that already
+ exists and makes sure properties are also removed (which is always the case as right
+ now address book objects never have properties).
+ """
+
+ # Create address book object
+ adbk1 = yield self.addressbookUnderTest()
+ name = "4.vcf"
+ component = VComponent.fromString(vcard4_text)
+ addressobject = yield adbk1.createAddressBookObjectWithName(name, component, metadata={})
+ resourceID = addressobject._resourceID
+
+ prop = schema.RESOURCE_PROPERTY
+ _allWithID = Select([prop.NAME, prop.VIEWER_UID, prop.VALUE],
+ From=prop,
+ Where=prop.RESOURCE_ID == Parameter("resourceID"))
+
+ # No properties on existing address book object
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+
+ yield self.commit()
+
+ # Remove address book object and check for no properties
+ adbk1 = yield self.addressbookUnderTest()
+ yield adbk1.removeAddressBookObjectWithName(name)
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
+ # Recheck it
+ rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
+ self.assertEqual(len(tuple(rows)), 0)
+ yield self.commit()
+
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -668,6 +668,7 @@
def cleanup():
try:
trash.remove()
+ self.properties()._removeResource()
except Exception, e:
self.log_error("Unable to delete trashed child at %s: %s" % (trash.fp, e))
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2011-03-11 17:10:42 UTC (rev 7183)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2011-03-11 19:38:43 UTC (rev 7184)
@@ -1655,6 +1655,8 @@
yield self._deletedSyncToken()
yield self._deleteQuery.on(self._txn, NoSuchHomeChildError,
resourceID=self._resourceID)
+ self.properties()._removeResource()
+
# Set to non-existent state
self._resourceID = None
self._created = None
@@ -1858,54 +1860,34 @@
returnValue(objectResource)
- @classproperty
- def _removeObjectResourceByNameQuery(cls):
- """
- DAL query to remove an object resource from this collection by name,
- returning the UID of the resource it deleted.
- """
- obj = cls._objectSchema
- return Delete(From=obj,
- Where=(obj.RESOURCE_NAME == Parameter("name")).And(
- obj.PARENT_RESOURCE_ID == Parameter("resourceID")),
- Return=obj.UID)
-
-
@inlineCallbacks
def removeObjectResourceWithName(self, name):
- uid = (yield self._removeObjectResourceByNameQuery.on(
- self._txn, NoSuchObjectResourceError,
- name=name, resourceID=self._resourceID))[0][0]
- self._objects.pop(name, None)
- self._objects.pop(uid, None)
- yield self._deleteRevision(name)
- self.notifyChanged()
+
+ child = (yield self.objectResourceWithName(name))
+ if child is None:
+ raise NoSuchObjectResourceError
+ yield self._removeObjectResource(child)
- @classproperty
- def _removeObjectResourceByUIDQuery(cls):
- """
- DAL query to remove an object resource from this collection by UID,
- returning the name of the resource it deleted.
- """
- obj = cls._objectSchema
- return Delete(From=obj,
- Where=(obj.UID == Parameter("uid")).And(
- obj.PARENT_RESOURCE_ID == Parameter("resourceID")),
- Return=obj.RESOURCE_NAME)
-
-
@inlineCallbacks
def removeObjectResourceWithUID(self, uid):
- name = (yield self._removeObjectResourceByUIDQuery.on(
- self._txn, NoSuchObjectResourceError,
- uid=uid, resourceID=self._resourceID
- ))[0][0]
- self._objects.pop(name, None)
- self._objects.pop(uid, None)
- yield self._deleteRevision(name)
+
+ child = (yield self.objectResourceWithUID(uid))
+ if child is None:
+ raise NoSuchObjectResourceError
+ yield self._removeObjectResource(child)
- self.notifyChanged()
+ @inlineCallbacks
+ def _removeObjectResource(self, child):
+ name = child.name()
+ uid = child.uid()
+ try:
+ yield child.remove()
+ finally:
+ self._objects.pop(name, None)
+ self._objects.pop(uid, None)
+ yield self._deleteRevision(name)
+ self.notifyChanged()
def objectResourcesHaveProperties(self):
@@ -2242,7 +2224,30 @@
def componentType(self):
returnValue((yield self.component()).mainType())
+ @classproperty
+ def _deleteQuery(cls):
+ """
+ DAL statement to delete a L{CommonObjectResource} by its resource ID.
+ """
+ return Delete(cls._objectSchema, Where=cls._objectSchema.RESOURCE_ID == Parameter("resourceID"))
+
+ @inlineCallbacks
+ def remove(self):
+ yield self._deleteQuery.on(self._txn, NoSuchObjectResourceError,
+ resourceID=self._resourceID)
+ self.properties()._removeResource()
+
+ # Set to non-existent state
+ self._resourceID = None
+ self._name = None
+ self._uid = None
+ self._md5 = None
+ self._size = None
+ self._created = None
+ self._modified = None
+ self._objectText = None
+
def uid(self):
return self._uid
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110311/0871f679/attachment-0001.html>
More information about the calendarserver-changes
mailing list