[CalendarServer-changes] [6463] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Mon Oct 25 09:48:53 PDT 2010
Revision: 6463
http://trac.macosforge.org/projects/calendarserver/changeset/6463
Author: cdaboo at apple.com
Date: 2010-10-25 09:48:50 -0700 (Mon, 25 Oct 2010)
Log Message:
-----------
Fixing up some missing yields. Re-worked object resource meta-data (md5, created, modified etc) to pre-cache from the DB when read, to
avoid having to use Deferred's to read them later. This allowed a bunch of yield's to be removed. API for object resource creation changed
to add an initFromStore method that handles all the meta-data pre-caching (as well as the property pre-cache too).
Modified Paths:
--------------
CalendarServer/trunk/twext/web2/dav/resource.py
CalendarServer/trunk/twext/web2/static.py
CalendarServer/trunk/twext/web2/stream.py
CalendarServer/trunk/twistedcaldav/extensions.py
CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py
CalendarServer/trunk/twistedcaldav/method/put_common.py
CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/txdav/base/datastore/file.py
CalendarServer/trunk/txdav/base/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/file.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/common.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/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql
CalendarServer/trunk/txdav/common/datastore/sql_tables.py
Modified: CalendarServer/trunk/twext/web2/dav/resource.py
===================================================================
--- CalendarServer/trunk/twext/web2/dav/resource.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twext/web2/dav/resource.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -231,7 +231,7 @@
returnValue(davxml.ResourceType.empty) #@UndefinedVariable
if name == "getetag":
- etag = yield self.etag()
+ etag = self.etag()
if etag is None:
returnValue(None)
returnValue(davxml.GETETag(etag.generate()))
@@ -253,13 +253,13 @@
returnValue(davxml.GETContentLength(str(length)))
if name == "getlastmodified":
- lastModified = yield self.lastModified()
+ lastModified = self.lastModified()
if lastModified is None:
returnValue(None)
returnValue(davxml.GETLastModified.fromDate(lastModified))
if name == "creationdate":
- creationDate = yield self.creationDate()
+ creationDate = self.creationDate()
if creationDate is None:
returnValue(None)
returnValue(davxml.CreationDate.fromDate(creationDate))
Modified: CalendarServer/trunk/twext/web2/static.py
===================================================================
--- CalendarServer/trunk/twext/web2/static.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twext/web2/static.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -105,8 +105,8 @@
http.checkPreconditions(
request,
entityExists = self.exists(),
- etag = (yield self.etag()),
- lastModified = (yield self.lastModified()),
+ etag = self.etag(),
+ lastModified = self.lastModified(),
)
# Check per-method preconditions
@@ -135,8 +135,8 @@
# (necessarily) to the resource content, so they depend on the
# request method, and therefore can't be set here.
for (header, value) in (
- ("etag", (yield self.etag())),
- ("last-modified", (yield self.lastModified())),
+ ("etag", self.etag()),
+ ("last-modified", self.lastModified()),
):
if value is not None:
response.headers.setHeader(header, value)
Modified: CalendarServer/trunk/twext/web2/stream.py
===================================================================
--- CalendarServer/trunk/twext/web2/stream.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twext/web2/stream.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -654,7 +654,7 @@
return defer.fail(f)
return None
else:
- deferred = self.deferred = Deferred()
+ deferred = self.deferred = Deferred(noInlineCallbackDebugging=True)
if self.producer is not None and (not self.streamingProducer
or self.producerPaused):
self.producerPaused = False
Modified: CalendarServer/trunk/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/extensions.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twistedcaldav/extensions.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -522,7 +522,7 @@
even = Alternator()
for name in sorted((yield self.listChildren())):
- child = self.getChild(name)
+ child = (yield maybeDeferred(self.getChild, name))
url, name, size, lastModified, contentType = self.getChildDirectoryEntry(child, name, request)
Modified: CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -33,8 +33,6 @@
from twext.web2.dav import davxml
from twext.web2.dav.element.base import dav_namespace
from twext.web2.dav.http import ErrorResponse
-from twext.web2.dav.resource import TwistedGETContentMD5
-from twext.web2.dav.stream import MD5StreamWrapper
from twext.web2.dav.util import joinURL, parentForURL
from twext.web2.http import HTTPError
from twext.web2.http import StatusResponse
@@ -376,14 +374,9 @@
if self.vcarddata is None:
self.vcarddata = str(self.vcard)
- md5 = MD5StreamWrapper(MemoryStream(self.vcarddata))
- response = (yield self.destination.storeStream(md5))
+ stream = MemoryStream(self.vcarddata)
+ response = (yield self.destination.storeStream(stream))
- # Finish MD5 calculation and write dead property
- md5.close()
- md5 = md5.getMD5()
- self.destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
-
returnValue(response)
@inlineCallbacks
Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -35,8 +35,6 @@
from twext.web2.dav.element.base import dav_namespace
from twext.web2.dav.element.base import PCDATAElement
-from twext.web2.dav.resource import TwistedGETContentMD5
-from twext.web2.dav.stream import MD5StreamWrapper
from twext.web2.http import HTTPError
from twext.web2.http import StatusResponse
from twext.web2.http_headers import generateContentType, MimeType
@@ -769,14 +767,9 @@
if self.calendardata is None:
self.calendardata = str(self.calendar)
- md5 = MD5StreamWrapper(MemoryStream(self.calendardata))
- response = yield self.destination.storeStream(md5)
+ stream = MemoryStream(self.calendardata)
+ response = yield self.destination.storeStream(stream)
- # Finish MD5 calculation and write dead property
- md5.close()
- md5 = md5.getMD5()
- self.destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
-
returnValue(response)
@inlineCallbacks
@@ -937,7 +930,7 @@
etags = self.destination.readDeadProperty(TwistedScheduleMatchETags).children
else:
etags = ()
- etags += (davxml.GETETag.fromString((yield self.destination.etag()).tag),)
+ etags += (davxml.GETETag.fromString(self.destination.etag().tag),)
self.destination.writeDeadProperty(TwistedScheduleMatchETags(*etags))
else:
self.destination.removeDeadProperty(TwistedScheduleMatchETags)
Modified: CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -31,7 +31,7 @@
from twext.web2.dav.util import joinURL
from twext.web2.http import HTTPError, StatusResponse
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, maybeDeferred
from twistedcaldav import carddavxml
from twistedcaldav.caldavxml import caldav_namespace
@@ -171,7 +171,8 @@
for href in resources:
resource_uri = str(href)
name = unquote(resource_uri[resource_uri.rfind("/") + 1:])
- if not self._isChildURI(request, resource_uri) or self.getChild(name) is None:
+ child = (yield maybeDeferred(self.getChild, name))
+ if not self._isChildURI(request, resource_uri) or child is None or not child.exists():
responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
else:
valid_names.append(name)
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -40,8 +40,7 @@
from twext.web2.dav.davxml import SyncCollection
from twext.web2.dav.http import ErrorResponse
-from twisted.internet import reactor
-from twisted.internet.defer import Deferred, succeed, maybeDeferred, fail
+from twisted.internet.defer import succeed, maybeDeferred, fail
from twisted.internet.defer import inlineCallbacks, returnValue
from twext.web2 import responsecode, http, http_headers
@@ -971,61 +970,6 @@
def findAddressBookCollections(self, depth, request, callback, privileges=None):
return self.findSpecialCollections(carddavxml.AddressBook, depth, request, callback, privileges)
- def findSpecialCollections(self, type, depth, request, callback, privileges=None):
- assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
-
- def checkPrivilegesError(failure):
- failure.trap(AccessDeniedError)
-
- reactor.callLater(0, getChild)
-
- def checkPrivileges(child):
- if privileges is None:
- return child
-
- ca = child.checkPrivileges(request, privileges)
- ca.addCallback(lambda ign: child)
- return ca
-
- def gotChild(child, childpath):
- if child.isSpecialCollection(type):
- callback(child, childpath)
-
- # No more regular collections
- #elif child.isCollection():
- # if depth == "infinity":
- # fc = child.findSpecialCollections(type, depth, request, callback, privileges)
- # fc.addCallback(lambda x: reactor.callLater(0, getChild))
- # return fc
-
- reactor.callLater(0, getChild)
-
- def getChild():
- try:
- childname = children.pop()
- except IndexError:
- completionDeferred.callback(None)
- else:
- childpath = joinURL(basepath, childname)
- child = request.locateResource(childpath)
- child.addCallback(checkPrivileges)
- child.addCallbacks(gotChild, checkPrivilegesError, (childpath,))
- child.addErrback(completionDeferred.errback)
-
- completionDeferred = Deferred()
-
- if depth != "0" and self.isCollection():
- basepath = request.urlForResource(self)
- children = []
- def gotChildren(childNames):
- children[:] = list(childNames)
- getChild()
- maybeDeferred(self.listChildren).addCallback(gotChildren)
- else:
- completionDeferred.callback(None)
-
- return completionDeferred
-
@inlineCallbacks
def findSpecialCollectionsFaster(self, type, depth, request, callback, privileges=None):
assert depth in ("0", "1", "infinity"), "Invalid depth: %s" % (depth,)
@@ -1037,7 +981,7 @@
child = (yield request.locateResource(childpath))
if privileges:
try:
- child.checkPrivileges(request, privileges)
+ yield child.checkPrivileges(request, privileges)
except AccessDeniedError:
continue
if child.isSpecialCollection(type):
@@ -1046,6 +990,8 @@
if depth == "infinity":
yield child.findSpecialCollectionsFaster(type, depth, request, callback, privileges)
+ findSpecialCollections = findSpecialCollectionsFaster
+
def createdCalendar(self, request):
"""
See L{ICalDAVResource.createCalendar}.
@@ -1118,7 +1064,6 @@
returnValue(False)
-
@inlineCallbacks
def iCalendarForUser(self, request, name=None):
if name is not None:
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -20,8 +20,6 @@
L{txdav.carddav.iaddressbookstore} and those in L{twistedcaldav}.
"""
-import hashlib
-
from urlparse import urlsplit
from twisted.internet.defer import succeed, inlineCallbacks, returnValue,\
@@ -56,7 +54,6 @@
from twistedcaldav.scheduling.implicit import ImplicitScheduler
from twistedcaldav.vcard import Component as VCard
-from txdav.common.icommondatastore import NoSuchObjectResourceError
from txdav.base.propertystore.base import PropertyName
log = Logger()
@@ -162,32 +159,10 @@
return self._newStoreObject.name() if self._newStoreObject is not None else None
- @inlineCallbacks
def etag(self):
- # FIXME: far too slow to be used for real, but I needed something to
- # placate the etag computation in the case where the file doesn't exist
- # yet (an uncommitted transaction creating this calendar file)
+ return ETag(self._newStoreObject.md5()) if self._newStoreObject is not None else None
- if self._newStoreObject is None:
- returnValue(None)
- # FIXME: direct tests
- try:
- md5 = yield self._newStoreObject.md5()
- if md5:
- returnValue(ETag(md5))
- else:
- returnValue(ETag(
- hashlib.new("md5", (yield self.text())).hexdigest(),
- weak=False
- ))
- except NoSuchObjectResourceError:
- # FIXME: a workaround for the fact that DELETE still rudely vanishes
- # the calendar object out from underneath the store, and doesn't
- # call storeRemove.
- returnValue(None)
-
-
def contentType(self):
return self._newStoreObject.contentType() if self._newStoreObject is not None else None
@@ -342,9 +317,8 @@
return self._newStoreCalendar.name()
- @inlineCallbacks
def etag(self):
- returnValue(ETag((yield self._newStoreCalendar.md5())))
+ return ETag(self._newStoreCalendar.md5())
def lastModified(self):
@@ -552,7 +526,7 @@
@inlineCallbacks
def listChildren(self):
l = []
- for attachment in (self._newStoreCalendarObject.attachments()):
+ for attachment in (yield self._newStoreCalendarObject.attachments()):
l.append(attachment.name())
returnValue(l)
@@ -670,11 +644,9 @@
self._newStoreAttachment = self._newStoreObject = attachment
- @inlineCallbacks
def etag(self):
# FIXME: test
- md5 = yield self._newStoreAttachment.md5()
- returnValue(ETag(md5))
+ return ETag(self._newStoreAttachment.md5())
def contentType(self):
@@ -758,9 +730,8 @@
return self._newStoreCalendar.name()
- @inlineCallbacks
def etag(self):
- returnValue(ETag((yield self._newStoreCalendar.md5())))
+ return ETag(self._newStoreCalendar.md5())
def lastModified(self):
@@ -1480,9 +1451,8 @@
return self._newStoreAddressBook.name()
- @inlineCallbacks
def etag(self):
- returnValue(ETag((yield self._newStoreAddressBook.md5())))
+ return ETag(self._newStoreAddressBook.md5())
def lastModified(self):
@@ -2140,29 +2110,9 @@
return True
- @inlineCallbacks
def etag(self):
- # FIXME: far too slow to be used for real, but I needed something to
- # placate the etag computation in the case where the file doesn't exist
- # yet (an uncommited transaction creating this calendar file)
+ return ETag(self._newStoreObject.md5())
- # FIXME: direct tests
- try:
- md5 = yield self._newStoreObject.md5()
- if md5:
- returnValue(ETag(md5))
- else:
- returnValue(ETag(
- hashlib.new("md5", (yield self.text())).hexdigest(),
- weak=False
- ))
- except NoSuchObjectResourceError:
- # FIXME: a workaround for the fact that DELETE still rudely vanishes
- # the calendar object out from underneath the store, and doesn't
- # call storeRemove.
- returnValue(None)
-
-
def contentType(self):
return self._newStoreObject.contentType()
Modified: CalendarServer/trunk/txdav/base/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/base/datastore/file.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/base/datastore/file.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -28,8 +28,8 @@
from twext.web2.dav.element.rfc2518 import GETContentType
from twext.web2.dav.resource import TwistedGETContentMD5
+from twisted.python import hashlib
-
from zope.interface.declarations import implements
def isValidName(name):
@@ -223,7 +223,15 @@
try:
return str(self.properties()[PropertyName.fromElement(TwistedGETContentMD5)])
except KeyError:
- return None
+ # FIXME: Strictly speaking we should not need to read the data as the md5 property should always be
+ # present. However, our unit tests use static files for their data store and those currently
+ # do not include the md5 xattr.
+ try:
+ data = self._path.open().read()
+ except IOError:
+ return None
+ md5 = hashlib.md5(data).hexdigest()
+ return md5
def size(self):
"""
Modified: CalendarServer/trunk/txdav/base/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/base/datastore/sql.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/base/datastore/sql.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -18,7 +18,7 @@
Logic common to SQL implementations.
"""
-from twisted.internet.defer import Deferred
+from twisted.internet.defer import Deferred, succeed
from inspect import getargspec
def _getarg(argname, argspec, args, kw):
@@ -54,19 +54,21 @@
-def memoized(keyArgument, memoAttribute):
+def memoized(keyArgument, memoAttribute, deferredResult=True):
"""
Decorator which memoizes the result of a method on that method's instance.
@param keyArgument: The name of the 'key' argument.
-
@type keyArgument: C{str}
@param memoAttribute: The name of the attribute on the instance which
should be used for memoizing the result of this method; the attribute
itself must be a dictionary.
-
@type memoAttribute: C{str}
+
+ @param deferredResult: Whether the result must be a deferred.
+ @type keyArgument: C{bool}
+
"""
def decorate(thunk):
# cheater move to try to get the right argspec from inlineCallbacks.
@@ -83,25 +85,17 @@
memo = getattr(self, memoAttribute)
key = _getarg(keyArgument, spec, a, kw)
if key in memo:
- result = memo[key]
- else:
- result = thunk(*a, **kw)
- if result is not None:
- memo[key] = result
+ memoed = memo[key]
+ return succeed(memoed) if deferredResult else memoed
+ result = thunk(*a, **kw)
if isinstance(result, Deferred):
- # clone the Deferred so that the old one keeps its result.
- # FIXME: cancellation?
- returnResult = Deferred()
- def relayAndPreserve(innerResult):
- if innerResult is None and key in memo and memo[key] is result:
- # The result was None, call it again.
- del memo[key]
- returnResult.callback(innerResult)
- return innerResult
- result.addBoth(relayAndPreserve)
- return returnResult
+ def memoResult(finalResult):
+ if finalResult is not None:
+ memo[key] = finalResult
+ return finalResult
+ result.addCallback(memoResult)
+ elif result is not None:
+ memo[key] = result
return result
return outer
return decorate
-
-
Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/file.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -69,6 +69,9 @@
from zope.interface import implements
+contentTypeKey = PropertyName.fromElement(GETContentType)
+md5key = PropertyName.fromElement(TwistedGETContentMD5)
+
CalendarStore = CommonDataStore
CalendarStoreTransaction = CommonStoreTransaction
@@ -250,14 +253,19 @@
if self._path.exists():
backup = hidden(self._path.temporarySibling())
self._path.moveTo(backup)
+
+ componentText = str(component)
fh = self._path.open("w")
try:
# FIXME: concurrency problem; if this write is interrupted
# halfway through, the underlying file will be corrupt.
- fh.write(str(component))
+ fh.write(componentText)
finally:
fh.close()
+ md5 = hashlib.md5(componentText).hexdigest()
+ self.properties()[md5key] = TwistedGETContentMD5.fromString(md5)
+
# Now re-write the original properties on the updated file
self.properties().flush()
@@ -427,9 +435,6 @@
-contentTypeKey = PropertyName.fromElement(GETContentType)
-md5key = PropertyName.fromElement(TwistedGETContentMD5)
-
class AttachmentStorageTransport(object):
implements(ITransport)
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -227,8 +227,12 @@
class CalendarObject(CommonObjectResource):
implements(ICalendarObject)
- _objectTable = CALENDAR_OBJECT_TABLE
+ def __init__(self, calendar, name, uid):
+ super(CalendarObject, self).__init__(calendar, name, uid)
+ self._objectTable = CALENDAR_OBJECT_TABLE
+
+
@property
def _calendar(self):
return self._parentCollection
@@ -294,14 +298,20 @@
organizer = ""
# CALENDAR_OBJECT table update
+ self._md5 = hashlib.md5(componentText).hexdigest()
+ self._size = len(componentText)
if inserting:
- self._resourceID = (yield self._txn.execSQL(
+ self._resourceID, self._created, self._modified = (
+ yield self._txn.execSQL(
"""
insert into CALENDAR_OBJECT
- (CALENDAR_RESOURCE_ID, RESOURCE_NAME, ICALENDAR_TEXT, ICALENDAR_UID, ICALENDAR_TYPE, ATTACHMENTS_MODE, ORGANIZER, RECURRANCE_MAX)
+ (CALENDAR_RESOURCE_ID, RESOURCE_NAME, ICALENDAR_TEXT, ICALENDAR_UID, ICALENDAR_TYPE, ATTACHMENTS_MODE, ORGANIZER, RECURRANCE_MAX, MD5)
values
- (%s, %s, %s, %s, %s, %s, %s, %s)
- returning RESOURCE_ID
+ (%s, %s, %s, %s, %s, %s, %s, %s, %s)
+ returning
+ RESOURCE_ID,
+ CREATED,
+ MODIFIED
""",
# FIXME: correct ATTACHMENTS_MODE based on X-APPLE-
# DROPBOX
@@ -314,16 +324,18 @@
_ATTACHMENTS_MODE_WRITE,
organizer,
normalizeForIndex(instances.limit) if instances.limit else None,
+ self._md5,
]
- ))[0][0]
+ ))[0]
else:
yield self._txn.execSQL(
"""
update CALENDAR_OBJECT set
- (ICALENDAR_TEXT, ICALENDAR_UID, ICALENDAR_TYPE, ATTACHMENTS_MODE, ORGANIZER, RECURRANCE_MAX, MODIFIED)
+ (ICALENDAR_TEXT, ICALENDAR_UID, ICALENDAR_TYPE, ATTACHMENTS_MODE, ORGANIZER, RECURRANCE_MAX, MD5, MODIFIED)
=
- (%s, %s, %s, %s, %s, %s, timezone('UTC', CURRENT_TIMESTAMP))
- where RESOURCE_ID = %s
+ (%s, %s, %s, %s, %s, %s, %s, timezone('UTC', CURRENT_TIMESTAMP))
+ where RESOURCE_ID = %s
+ returning MODIFIED
""",
# should really be filling out more fields: ORGANIZER,
# ORGANIZER_OBJECT, a correct ATTACHMENTS_MODE based on X-APPLE-
@@ -335,7 +347,8 @@
_ATTACHMENTS_MODE_WRITE,
organizer,
normalizeForIndex(instances.limit) if instances.limit else None,
- self._resourceID
+ self._md5,
+ self._resourceID,
]
)
@@ -485,12 +498,9 @@
@inlineCallbacks
def attachmentWithName(self, name):
attachment = Attachment(self, name)
- if (yield attachment._populate()):
- returnValue(attachment)
- else:
- returnValue(None)
+ attachment = (yield attachment.initFromStore())
+ returnValue(attachment)
-
@inlineCallbacks
def attendeesCanManageAttachments(self):
returnValue((yield self.component()).hasPropertyInAnyComponent(
@@ -569,15 +579,23 @@
@inlineCallbacks
def loseConnection(self):
self.attachment._path.setContent(self.buf)
- contentTypeString = generateContentType(self.contentType)
- yield self._txn.execSQL(
+ self.attachment._contentType = self.contentType
+ self.attachment._md5 = self.hash.hexdigest()
+ self.attachment._size = len(self.buf)
+ self.attachment._created, self.attachment._modified = (yield self._txn.execSQL(
"""
update ATTACHMENT set CONTENT_TYPE = %s, SIZE = %s, MD5 = %s,
- MODIFIED = timezone('UTC', CURRENT_TIMESTAMP) WHERE PATH = %s
+ MODIFIED = timezone('UTC', CURRENT_TIMESTAMP)
+ where PATH = %s
+ returning CREATED, MODIFIED
""",
- [contentTypeString, len(self.buf),
- self.hash.hexdigest(), self.attachment.name()]
- )
+ [
+ generateContentType(self.contentType),
+ self.attachment._size,
+ self.attachment._md5,
+ self.attachment.name()
+ ]
+ ))[0]
@@ -596,7 +614,7 @@
@inlineCallbacks
- def _populate(self):
+ def initFromStore(self):
"""
Execute necessary SQL queries to retrieve attributes.
@@ -609,13 +627,13 @@
[self._name]
)
if not rows:
- returnValue(False)
+ returnValue(None)
self._contentType = MimeType.fromString(rows[0][0])
self._size = rows[0][1]
self._md5 = rows[0][2]
self._created = datetimeMktime(datetime.datetime.strptime(rows[0][3], "%Y-%m-%d %H:%M:%S.%f"))
self._modified = datetimeMktime(datetime.datetime.strptime(rows[0][4], "%Y-%m-%d %H:%M:%S.%f"))
- returnValue(True)
+ returnValue(self)
def name(self):
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -295,13 +295,16 @@
@inlineCallbacks
- def test_notificationObjectModified(self):
+ def test_notificationObjectMetaData(self):
"""
- The objects retrieved from the notification home have a C{modified}
- method which returns the timestamp of their last modification.
+ The objects retrieved from the notification home have various
+ methods which return metadata values.
"""
notification = yield self.notificationUnderTest()
- self.assertIsInstance((yield notification.modified()), int)
+ self.assertIsInstance(notification.md5(), basestring)
+ self.assertIsInstance(notification.size(), int)
+ self.assertIsInstance(notification.created(), int)
+ self.assertIsInstance(notification.modified(), int)
@inlineCallbacks
@@ -650,6 +653,21 @@
@inlineCallbacks
+ def test_calendarObjectMetaData(self):
+ """
+ The objects retrieved from the calendar have a variou
+ methods which return metadata values.
+ """
+ calendar = yield self.calendarObjectUnderTest()
+ self.assertIsInstance(calendar.name(), basestring)
+ self.assertIsInstance(calendar.uid(), basestring)
+ self.assertIsInstance(calendar.md5(), basestring)
+ self.assertIsInstance(calendar.size(), int)
+ self.assertIsInstance(calendar.created(), int)
+ self.assertIsInstance(calendar.modified(), int)
+
+
+ @inlineCallbacks
def test_component(self):
"""
L{ICalendarObject.component} returns a L{VComponent} describing the
Modified: CalendarServer/trunk/txdav/carddav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/file.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/carddav/datastore/file.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -29,8 +29,11 @@
from errno import ENOENT
-from twext.web2.dav.element.rfc2518 import ResourceType
+from twext.web2.dav.element.rfc2518 import ResourceType, GETContentType
+from twext.web2.dav.resource import TwistedGETContentMD5
+from twisted.python import hashlib
+
from twistedcaldav.sharing import InvitesDatabase
from twistedcaldav.vcard import Component as VComponent, InvalidVCardDataError
from txdav.carddav.datastore.index_file import AddressBookIndex as OldIndex
@@ -50,6 +53,9 @@
from zope.interface import implements
+contentTypeKey = PropertyName.fromElement(GETContentType)
+md5key = PropertyName.fromElement(TwistedGETContentMD5)
+
AddressBookStore = CommonDataStore
AddressBookStoreTransaction = CommonStoreTransaction
@@ -177,14 +183,19 @@
if self._path.exists():
backup = hidden(self._path.temporarySibling())
self._path.moveTo(backup)
+
+ componentText = str(component)
fh = self._path.open("w")
try:
# FIXME: concurrency problem; if this write is interrupted
# halfway through, the underlying file will be corrupt.
- fh.write(str(component))
+ fh.write(componentText)
finally:
fh.close()
+ md5 = hashlib.md5(componentText).hexdigest()
+ self.properties()[md5key] = TwistedGETContentMD5.fromString(md5)
+
# Now re-write the original properties on the updated file
self.properties().flush()
Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -28,6 +28,7 @@
from zope.interface.declarations import implements
from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.python import hashlib
from twext.web2.dav.element.rfc2518 import ResourceType
from twext.web2.http_headers import MimeType
@@ -160,9 +161,9 @@
implements(IAddressBookObject)
- def __init__(self, name, addressbook, resid, uid):
+ def __init__(self, addressbook, name, uid):
- super(AddressBookObject, self).__init__(name, addressbook, resid, uid)
+ super(AddressBookObject, self).__init__(addressbook, name, uid)
self._objectTable = ADDRESSBOOK_OBJECT_TABLE
@@ -201,35 +202,44 @@
self._objectText = componentText
# ADDRESSBOOK_OBJECT table update
+ self._md5 = hashlib.md5(componentText).hexdigest()
+ self._size = len(componentText)
if inserting:
- self._resourceID = (yield self._txn.execSQL(
+ self._resourceID, self._created, self._modified = (
+ yield self._txn.execSQL(
"""
insert into ADDRESSBOOK_OBJECT
- (ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME, VCARD_TEXT, VCARD_UID)
+ (ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME, VCARD_TEXT, VCARD_UID, MD5)
values
- (%s, %s, %s, %s)
- returning RESOURCE_ID
+ (%s, %s, %s, %s, %s)
+ returning
+ RESOURCE_ID,
+ CREATED,
+ MODIFIED
""",
[
self._addressbook._resourceID,
self._name,
componentText,
component.resourceUID(),
+ self._md5,
]
- ))[0][0]
+ ))[0]
else:
yield self._txn.execSQL(
"""
update ADDRESSBOOK_OBJECT set
- (VCARD_TEXT, VCARD_UID, MODIFIED)
+ (VCARD_TEXT, VCARD_UID, MD5, MODIFIED)
=
- (%s, %s, timezone('UTC', CURRENT_TIMESTAMP))
- where RESOURCE_ID = %s
+ (%s, %s, %s, timezone('UTC', CURRENT_TIMESTAMP))
+ where RESOURCE_ID = %s
+ returning MODIFIED
""",
[
componentText,
component.resourceUID(),
- self._resourceID
+ self._md5,
+ self._resourceID,
]
)
Modified: CalendarServer/trunk/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/common.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/common.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -538,6 +538,21 @@
@inlineCallbacks
+ def test_addressbookObjectMetaData(self):
+ """
+ The objects retrieved from the addressbook have various
+ methods which return metadata values.
+ """
+ adbk = yield self.addressbookObjectUnderTest()
+ self.assertIsInstance(adbk.name(), basestring)
+ self.assertIsInstance(adbk.uid(), basestring)
+ self.assertIsInstance(adbk.md5(), basestring)
+ self.assertIsInstance(adbk.size(), int)
+ self.assertIsInstance(adbk.created(), int)
+ self.assertIsInstance(adbk.modified(), int)
+
+
+ @inlineCallbacks
def test_component(self):
"""
L{IAddressBookObject.component} returns a L{VComponent} describing the
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -23,8 +23,10 @@
from twext.web2.dav.element.rfc2518 import ResourceType, GETContentType, HRef
from twext.web2.dav.element.rfc5842 import ResourceID
from twext.web2.http_headers import generateContentType, MimeType
+from twext.web2.dav.resource import TwistedGETContentMD5
from twisted.python.util import FancyEqMixin
+from twisted.python import hashlib
from twistedcaldav import customxml
from twistedcaldav.customxml import NotificationType
@@ -945,6 +947,12 @@
return self._parentCollection
+ def created(self):
+ if not self._path.exists():
+ from twisted.internet import reactor
+ return int(reactor.seconds())
+ return super(NotificationObject, self).created()
+
def modified(self):
if not self._path.exists():
from twisted.internet import reactor
@@ -961,6 +969,7 @@
)
self._xmldata = xmldata
+ md5 = hashlib.md5(xmldata).hexdigest()
def do():
backup = None
@@ -991,6 +1000,7 @@
props = self.properties()
props[PropertyName(*GETContentType.qname())] = GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset":"utf-8"})))
props[PropertyName.fromElement(NotificationType)] = NotificationType(xmltype)
+ props[PropertyName.fromElement(TwistedGETContentMD5)] = TwistedGETContentMD5.fromString(md5)
# FIXME: the property store's flush() method may already have been
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -619,7 +619,7 @@
else:
notifier = None
child = self._childClass(self, name, resourceID, notifier)
- yield child._loadPropertyStore()
+ yield child.initFromStore()
returnValue(child)
@@ -863,6 +863,8 @@
self._home = home
self._name = name
self._resourceID = resourceID
+ self._created = None
+ self._modified = None
self._objects = {}
self._notifier = notifier
@@ -870,6 +872,21 @@
self._invites = None # Derived classes need to set this
+ @inlineCallbacks
+ def initFromStore(self):
+ """
+ Initialise this object from the store. We read in and cache all the extra metadata
+ from the DB to avoid having to do DB queries for those individually later.
+ """
+
+ self._created, self._modified = (yield self._txn.execSQL(
+ "select %(column_CREATED)s, %(column_MODIFIED)s from %(name)s "
+ "where %(column_RESOURCE_ID)s = %%s" % self._homeChildTable,
+ [self._resourceID]
+ ))[0]
+
+ yield self._loadPropertyStore()
+
@property
def _txn(self):
return self._home._txn
@@ -940,48 +957,26 @@
@memoized('name', '_objects')
- @inlineCallbacks
def objectResourceWithName(self, name):
- rows = yield self._txn.execSQL(
- "select %(column_RESOURCE_ID)s, %(column_UID)s from %(name)s "
- "where %(column_RESOURCE_NAME)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
- [name, self._resourceID]
- )
- if not rows:
- returnValue(None)
- [resid, uid] = rows[0]
- returnValue((yield self._makeObjectResource(name, resid, uid)))
+ return self._makeObjectResource(name, None)
+ @memoized('uid', '_objects')
+ def objectResourceWithUID(self, uid):
+ return self._makeObjectResource(None, uid)
+
+
@inlineCallbacks
- def _makeObjectResource(self, name, resid, uid):
+ def _makeObjectResource(self, name, uid):
"""
- Create an instance of C{self._objectResourceClass}.
+ We create the empty object first then have it initialize itself from the store
"""
- objectResource = yield self._objectResourceClass(
- name, self, resid, uid
- )
- yield objectResource._loadPropertyStore()
+ objectResource = self._objectResourceClass(self, name, uid)
+ objectResource = (yield objectResource.initFromStore())
returnValue(objectResource)
- @memoized('uid', '_objects')
@inlineCallbacks
- def objectResourceWithUID(self, uid):
- rows = yield self._txn.execSQL(
- "select %(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s "
- "from %(name)s where %(column_UID)s = %%s "
- "and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
- [uid, self._resourceID]
- )
- if not rows:
- returnValue(None)
- resid = rows[0][0]
- name = rows[0][1]
- returnValue((yield self._makeObjectResource(name, resid, uid)))
-
-
- @inlineCallbacks
def createObjectResourceWithName(self, name, component):
if name.startswith("."):
raise ObjectResourceNameNotAllowedError(name)
@@ -995,9 +990,7 @@
if rows:
raise ObjectResourceNameAlreadyExistsError()
- objectResource = (
- yield self._makeObjectResource(name, None, component.resourceUID())
- )
+ objectResource = self._objectResourceClass(self, name, None)
yield objectResource.setComponent(component, inserting=True)
# Note: setComponent triggers a notification, so we don't need to
@@ -1261,26 +1254,14 @@
return 0
- @inlineCallbacks
def created(self):
- created = (yield self._txn.execSQL(
- "select %(column_CREATED)s from %(name)s "
- "where %(column_RESOURCE_ID)s = %%s" % self._homeChildTable,
- [self._resourceID]
- ))[0][0]
- utc = datetime.datetime.strptime(created, "%Y-%m-%d %H:%M:%S.%f")
- returnValue(datetimeMktime(utc))
+ utc = datetime.datetime.strptime(self._created, "%Y-%m-%d %H:%M:%S.%f")
+ return datetimeMktime(utc)
- @inlineCallbacks
def modified(self):
- modified = (yield self._txn.execSQL(
- "select %(column_MODIFIED)s from %(name)s "
- "where %(column_RESOURCE_ID)s = %%s" % self._homeChildTable,
- [self._resourceID]
- ))[0][0]
- utc = datetime.datetime.strptime(modified, "%Y-%m-%d %H:%M:%S.%f")
- returnValue(datetimeMktime(utc))
+ utc = datetime.datetime.strptime(self._modified, "%Y-%m-%d %H:%M:%S.%f")
+ return datetimeMktime(utc)
def notifierID(self, label="default"):
@@ -1310,15 +1291,73 @@
_objectTable = None
- def __init__(self, name, parent, resid, uid):
+ def __init__(self, parent, name, uid):
+ self._parentCollection = parent
+ self._resourceID = None
self._name = name
- self._parentCollection = parent
- self._resourceID = resid
+ self._uid = uid
+ self._md5 = None
+ self._size = None
+ self._created = None
+ self._modified = None
self._objectText = None
- self._uid = uid
@inlineCallbacks
+ def initFromStore(self):
+ """
+ Initialise this object from the store. We read in and cache all the extra metadata
+ from the DB to avoid having to do DB queries for those individually later. Either the
+ name or uid is present, so we have to tweak the query accordingly.
+
+ @return: L{self} if object exists in the DB, else C{None}
+ """
+
+ if self._name:
+ rows = yield self._txn.execSQL("""
+ select
+ %(column_RESOURCE_ID)s,
+ %(column_RESOURCE_NAME)s,
+ %(column_UID)s,
+ %(column_MD5)s,
+ character_length(%(column_TEXT)s),
+ %(column_CREATED)s,
+ %(column_MODIFIED)s
+ from %(name)s
+ where %(column_RESOURCE_NAME)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s
+ """ % self._objectTable,
+ [self._name, self._parentCollection._resourceID]
+ )
+ else:
+ rows = yield self._txn.execSQL("""
+ select
+ %(column_RESOURCE_ID)s,
+ %(column_RESOURCE_NAME)s,
+ %(column_UID)s,
+ %(column_MD5)s,
+ character_length(%(column_TEXT)s),
+ %(column_CREATED)s,
+ %(column_MODIFIED)s
+ from %(name)s
+ where %(column_UID)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s
+ """ % self._objectTable,
+ [self._uid, self._parentCollection._resourceID]
+ )
+ if rows:
+ (self._resourceID,
+ self._name,
+ self._uid,
+ self._md5,
+ self._size,
+ self._created,
+ self._modified,) = tuple(rows[0])
+ yield self._loadPropertyStore()
+ returnValue(self)
+ else:
+ returnValue(None)
+
+
+ @inlineCallbacks
def _loadPropertyStore(self):
props = yield PropertyStore.load(
self.uid(),
@@ -1378,39 +1417,21 @@
def md5(self):
- return None
+ return self._md5
- @inlineCallbacks
def size(self):
- size = (yield self._txn.execSQL(
- "select character_length(%(column_TEXT)s) from %(name)s "
- "where %(column_RESOURCE_ID)s = %%s" % self._objectTable,
- [self._resourceID]
- ))[0][0]
- returnValue(size)
+ return self._size
- @inlineCallbacks
def created(self):
- created = (yield self._txn.execSQL(
- "select %(column_CREATED)s from %(name)s "
- "where %(column_RESOURCE_ID)s = %%s" % self._objectTable,
- [self._resourceID]
- ))[0][0]
- utc = datetime.datetime.strptime(created, "%Y-%m-%d %H:%M:%S.%f")
- returnValue(datetimeMktime(utc))
+ utc = datetime.datetime.strptime(self._created, "%Y-%m-%d %H:%M:%S.%f")
+ return datetimeMktime(utc)
- @inlineCallbacks
def modified(self):
- modified = (yield self._txn.execSQL(
- "select %(column_MODIFIED)s from %(name)s "
- "where %(column_RESOURCE_ID)s = %%s" % self._objectTable,
- [self._resourceID]
- ))[0][0]
- utc = datetime.datetime.strptime(modified, "%Y-%m-%d %H:%M:%S.%f")
- returnValue(datetimeMktime(utc))
+ utc = datetime.datetime.strptime(self._modified, "%Y-%m-%d %H:%M:%S.%f")
+ return datetimeMktime(utc)
@inlineCallbacks
@@ -1502,17 +1523,13 @@
@memoized('uid', '_notifications')
@inlineCallbacks
def notificationObjectWithUID(self, uid):
- rows = (yield self._txn.execSQL(
- "select RESOURCE_ID from NOTIFICATION "
- "where NOTIFICATION_UID = %s and NOTIFICATION_HOME_RESOURCE_ID = %s",
- [uid, self._resourceID]))
- if rows:
- resourceID = rows[0][0]
- no = NotificationObject(self, uid, resourceID)
- yield no._loadPropertyStore()
- returnValue(no)
- else:
- returnValue(None)
+ """
+ We create the empty object first then have it initialize itself from the store
+ """
+
+ no = NotificationObject(self, uid)
+ no = (yield no.initFromStore())
+ returnValue(no)
@inlineCallbacks
@@ -1521,7 +1538,7 @@
inserting = False
notificationObject = yield self.notificationObjectWithUID(uid)
if notificationObject is None:
- notificationObject = NotificationObject(self, uid, None)
+ notificationObject = NotificationObject(self, uid)
inserting = True
yield notificationObject.setData(uid, xmltype, xmldata, inserting=inserting)
if inserting:
@@ -1693,15 +1710,47 @@
compareAttributes = '_resourceID _home'.split()
- def __init__(self, home, uid, resourceID):
+ def __init__(self, home, uid):
self._home = home
self._uid = uid
- self._resourceID = resourceID
+ self._resourceID = None
+ self._md5 = None
+ self._size = None
+ self._created = None
+ self._modified = None
-
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self._resourceID)
+ @inlineCallbacks
+ def initFromStore(self):
+ """
+ Initialise this object from the store. We read in and cache all the extra metadata
+ from the DB to avoid having to do DB queries for those individually later.
+
+ @return: L{self} if object exists in the DB, else C{None}
+ """
+ rows = (yield self._txn.execSQL("""
+ select
+ RESOURCE_ID,
+ MD5,
+ character_length(XML_DATA),
+ CREATED,
+ MODIFIED
+ from NOTIFICATION
+ where NOTIFICATION_UID = %s and NOTIFICATION_HOME_RESOURCE_ID = %s
+ """,
+ [self._uid, self._home._resourceID]))
+ if rows:
+ (self._resourceID,
+ self._md5,
+ self._size,
+ self._created,
+ self._modified,) = tuple(rows[0])
+ yield self._loadPropertyStore()
+ returnValue(self)
+ else:
+ returnValue(None)
@property
def _txn(self):
@@ -1722,21 +1771,37 @@
@inlineCallbacks
def setData(self, uid, xmltype, xmldata, inserting=False):
+ """
+ Set the object resource data and update and cached metadata.
+ """
xmltypeString = xmltype.toxml()
+ self._md5 = hashlib.md5(xmldata).hexdigest()
+ self._size = len(xmldata)
if inserting:
- rows = yield self._txn.execSQL(
- "insert into NOTIFICATION (NOTIFICATION_HOME_RESOURCE_ID, NOTIFICATION_UID, XML_TYPE, XML_DATA) "
- "values (%s, %s, %s, %s) returning RESOURCE_ID",
- [self._home._resourceID, uid, xmltypeString, xmldata]
+ rows = yield self._txn.execSQL("""
+ insert into NOTIFICATION
+ (NOTIFICATION_HOME_RESOURCE_ID, NOTIFICATION_UID, XML_TYPE, XML_DATA, MD5)
+ values
+ (%s, %s, %s, %s, %s)
+ returning
+ RESOURCE_ID,
+ CREATED,
+ MODIFIED
+ """,
+ [self._home._resourceID, uid, xmltypeString, xmldata, self._md5]
)
- self._resourceID = rows[0][0]
+ self._resourceID, self._created, self._modified = rows[0]
yield self._loadPropertyStore()
else:
- yield self._txn.execSQL(
- "update NOTIFICATION set XML_TYPE = %s, XML_DATA = %s "
- "where NOTIFICATION_HOME_RESOURCE_ID = %s and NOTIFICATION_UID = %s",
- [xmltypeString, xmldata, self._home._resourceID, uid])
+ rows = yield self._txn.execSQL("""
+ update NOTIFICATION
+ set XML_TYPE = %s, XML_DATA = %s, MD5 = %s
+ where NOTIFICATION_HOME_RESOURCE_ID = %s and NOTIFICATION_UID = %s
+ returning MODIFIED
+ """,
+ [xmltypeString, xmldata, self._md5, self._home._resourceID, uid])
+ self._modified = rows[0][0]
self.properties()[PropertyName.fromElement(NotificationType)] = NotificationType(xmltype)
@@ -1787,40 +1852,22 @@
return MimeType.fromString("text/xml")
- @inlineCallbacks
def md5(self):
- returnValue(hashlib.md5((yield self.xmldata())).hexdigest())
+ return self._md5
- @inlineCallbacks
def size(self):
- size = (yield self._txn.execSQL(
- "select character_length(XML_DATA) from NOTIFICATION "
- "where RESOURCE_ID = %s",
- [self._resourceID]
- ))[0][0]
- returnValue(size)
+ return self._size
- @inlineCallbacks
def created(self):
- created = (yield self._txn.execSQL(
- "select CREATED from NOTIFICATION "
- "where RESOURCE_ID = %s",
- [self._resourceID]
- ))[0][0]
- utc = datetime.datetime.strptime(created, "%Y-%m-%d %H:%M:%S.%f")
- returnValue(datetimeMktime(utc))
+ utc = datetime.datetime.strptime(self._created, "%Y-%m-%d %H:%M:%S.%f")
+ return datetimeMktime(utc)
- @inlineCallbacks
def modified(self):
- modified = (yield self._txn.execSQL(
- "select MODIFIED from NOTIFICATION "
- "where RESOURCE_ID = %s", [self._resourceID]
- ))[0][0]
- utc = datetime.datetime.strptime(modified, "%Y-%m-%d %H:%M:%S.%f")
- returnValue(datetimeMktime(utc))
+ utc = datetime.datetime.strptime(self._modified, "%Y-%m-%d %H:%M:%S.%f")
+ return datetimeMktime(utc)
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql 2010-10-25 16:48:50 UTC (rev 6463)
@@ -55,6 +55,7 @@
NOTIFICATION_UID varchar(255) not null,
XML_TYPE varchar not null,
XML_DATA varchar not null,
+ MD5 char(32) not null,
CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
@@ -126,6 +127,7 @@
ORGANIZER varchar(255),
ORGANIZER_OBJECT integer references CALENDAR_OBJECT,
RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ MD5 char(32) not null,
CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
@@ -292,6 +294,7 @@
RESOURCE_NAME varchar(255) not null,
VCARD_TEXT text not null,
VCARD_UID varchar(255) not null,
+ MD5 char(32) not null,
CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
Modified: CalendarServer/trunk/txdav/common/datastore/sql_tables.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_tables.py 2010-10-25 16:38:03 UTC (rev 6462)
+++ CalendarServer/trunk/txdav/common/datastore/sql_tables.py 2010-10-25 16:48:50 UTC (rev 6463)
@@ -113,6 +113,7 @@
"column_RESOURCE_NAME" : "RESOURCE_NAME",
"column_TEXT" : "ICALENDAR_TEXT",
"column_UID" : "ICALENDAR_UID",
+ "column_MD5" : "MD5",
"column_CREATED" : "CREATED",
"column_MODIFIED" : "MODIFIED",
}
@@ -124,6 +125,7 @@
"column_RESOURCE_NAME" : "RESOURCE_NAME",
"column_TEXT" : "VCARD_TEXT",
"column_UID" : "VCARD_UID",
+ "column_MD5" : "MD5",
"column_CREATED" : "CREATED",
"column_MODIFIED" : "MODIFIED",
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20101025/2932618e/attachment-0001.html>
More information about the calendarserver-changes
mailing list