[CalendarServer-changes] [6352] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Thu Sep 23 08:37:08 PDT 2010
Revision: 6352
http://trac.macosforge.org/projects/calendarserver/changeset/6352
Author: cdaboo at apple.com
Date: 2010-09-23 08:37:06 -0700 (Thu, 23 Sep 2010)
Log Message:
-----------
Allow sync report on notification collections.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/storebridge.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/twistedcaldav/method/report_sync_collection.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py 2010-09-23 15:36:14 UTC (rev 6351)
+++ CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py 2010-09-23 15:37:06 UTC (rev 6352)
@@ -46,8 +46,12 @@
"""
Generate a sync-collection REPORT.
"""
- if not self.isPseudoCalendarCollection() and not self.isAddressBookCollection() or not config.EnableSyncReport:
- log.err("sync-collection report is only allowed on calendar/inbox/addressbook collection resources %s" % (self,))
+ if not config.EnableSyncReport or (
+ not self.isPseudoCalendarCollection() and
+ not self.isAddressBookCollection() and
+ not self.isNotificationCollection()
+ ):
+ log.err("sync-collection report is only allowed on calendar/inbox/addressbook/notification collection resources %s" % (self,))
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, davxml.SupportedReport()))
responses = []
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2010-09-23 15:36:14 UTC (rev 6351)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2010-09-23 15:37:06 UTC (rev 6352)
@@ -930,6 +930,12 @@
"""
return self.isSpecialCollection(carddavxml.AddressBook)
+ def isNotificationCollection(self):
+ """
+ See L{ICalDAVResource.isNotificationCollection}.
+ """
+ return self.isSpecialCollection(customxml.Notification)
+
def isDirectoryBackedAddressBookCollection(self): # ATM - temporary fix? (this one worked)
return False
@@ -1202,8 +1208,12 @@
if config.EnableCardDAV:
result.append(davxml.Report(carddavxml.AddressBookQuery(),))
result.append(davxml.Report(carddavxml.AddressBookMultiGet(),))
- if (self.isPseudoCalendarCollection() or self.isAddressBookCollection()) and config.EnableSyncReport:
- # Only allowed on calendar/inbox/addressbook collections
+ if (
+ self.isPseudoCalendarCollection() or
+ self.isAddressBookCollection() or
+ self.isNotificationCollection()
+ ) and config.EnableSyncReport:
+ # Only allowed on calendar/inbox/addressbook/notification collections
result.append(davxml.Report(SyncCollection(),))
return result
@@ -1332,12 +1342,15 @@
revision = 0
try:
- changed, removed = self.index().whatchanged(revision)
+ changed, removed = self._indexWhatChanged(revision)
except SyncTokenValidException:
raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (dav_namespace, "valid-sync-token")))
return changed, removed, current_token
+ def _indexWhatChanged(self, revision):
+ return self.index().whatchanged(revision)
+
def getSyncToken(self):
"""
Return current sync-token value.
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2010-09-23 15:36:14 UTC (rev 6351)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2010-09-23 15:37:06 UTC (rev 6352)
@@ -1837,6 +1837,12 @@
def isCollection(self):
return True
+ def getSyncToken(self):
+ return self._newStoreNotifications.syncToken()
+
+ def _indexWhatChanged(self, revision):
+ return self._newStoreNotifications.resourceNamesSinceToken(revision)
+
def addNotification(self, request, uid, xmltype, xmldata):
self._newStoreNotifications.writeNotificationObject(uid, xmltype, xmldata)
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2010-09-23 15:36:14 UTC (rev 6351)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2010-09-23 15:37:06 UTC (rev 6352)
@@ -44,7 +44,7 @@
from txdav.common.datastore.sql_tables import CALENDAR_HOME_TABLE, \
ADDRESSBOOK_HOME_TABLE, NOTIFICATION_HOME_TABLE, _BIND_MODE_OWN, \
- _BIND_STATUS_ACCEPTED
+ _BIND_STATUS_ACCEPTED, NOTIFICATION_OBJECT_REVISIONS_TABLE
from txdav.common.icommondatastore import HomeChildNameNotAllowedError, \
HomeChildNameAlreadyExistsError, NoSuchHomeChildError, \
ObjectResourceNameNotAllowedError, ObjectResourceNameAlreadyExistsError, \
@@ -242,14 +242,18 @@
)
if rows:
resourceID = rows[0][0]
+ created = False
else:
resourceID = str(self.execSQL(
"insert into %(name)s (%(column_OWNER_UID)s) values (%%s) returning %(column_RESOURCE_ID)s" % NOTIFICATION_HOME_TABLE,
[uid]
)[0][0])
- return NotificationCollection(self, uid, resourceID)
+ created = True
+ collection = NotificationCollection(self, uid, resourceID)
+ if created:
+ collection._initSyncToken()
+ return collection
-
def abort(self):
if not self._completed:
# print 'ABORTING', self._label
@@ -651,10 +655,10 @@
def _initSyncToken(self):
self._txn.execSQL("""
insert into %(name)s
- (%(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
- values (%%s, %%s, nextval('%(sequence)s'), FALSE)
+ (%(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
+ values (%%s, %%s, %%s, nextval('%(sequence)s'), FALSE)
""" % self._revisionsTable,
- [self._resourceID, ""]
+ [self._home._resourceID, self._resourceID, ""]
)
def syncToken(self):
@@ -670,6 +674,30 @@
def objectResourcesSinceToken(self, token):
raise NotImplementedError()
+ def resourceNamesSinceToken(self, token):
+ results = [
+ (name.encode("utf-8"), deleted)
+ for name, deleted in
+ self._txn.execSQL("""
+ select %(column_RESOURCE_NAME)s, %(column_DELETED)s from %(name)s
+ where %(column_REVISION)s > %s and %(column_RESOURCE_ID)s = %s
+ """ % self._revisionsTable,
+ [token, self._resourceID],
+ )
+ ]
+ results.sort(key=lambda x:x[1])
+
+ changed = []
+ deleted = []
+ for name, wasdeleted in results:
+ if name:
+ if wasdeleted:
+ if token:
+ deleted.append(name)
+ else:
+ changed.append(name)
+
+ return changed, deleted,
def _updateSyncToken(self):
@@ -750,10 +778,10 @@
else:
self._txn.execSQL("""
insert into %(name)s
- (%(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
- values (%%s, %%s, %%s, FALSE)
+ (%(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
+ values (%%s, %%s, %%s, %%s, FALSE)
""" % self._revisionsTable,
- [self._resourceID, name, nextrevision]
+ [self._home._resourceID, self._resourceID, name, nextrevision]
)
self._txn.execSQL("""
update %(name)s
@@ -762,6 +790,7 @@
""" % self._revisionsTable,
[nextrevision, self._resourceID, ""]
)
+
@cached
def properties(self):
props = PropertyStore(
@@ -928,10 +957,7 @@
compareAttributes = '_uid _resourceID'.split()
_objectResourceClass = None
- _bindTable = None
- _homeChildTable = None
- _revisionsTable = None
- _objectTable = None
+ _revisionsTable = NOTIFICATION_OBJECT_REVISIONS_TABLE
def __init__(self, txn, uid, resourceID):
@@ -999,8 +1025,11 @@
notificationObject = NotificationObject(self, None)
inserting = True
notificationObject.setData(uid, xmltype, xmldata, inserting=inserting)
+ if inserting:
+ self._insertRevision("%s.xml" % (uid,))
+ else:
+ self._updateRevision("%s.xml" % (uid,))
-
def removeNotificationObjectWithName(self, name):
self.removeNotificationObjectWithUID(self._nameToUID(name))
@@ -1012,12 +1041,32 @@
[uid, self._resourceID]
)
self._notifications.pop(uid, None)
+ self._deleteRevision("%s.xml" % (uid,))
+ def _initSyncToken(self):
+ self._txn.execSQL("""
+ insert into %(name)s
+ (%(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
+ values (%%s, %%s, nextval('%(sequence)s'), FALSE)
+ """ % self._revisionsTable,
+ [self._resourceID, ""]
+ )
+
def syncToken(self):
- return 'dummy-sync-token'
+ revision = self._txn.execSQL(
+ """
+ select %(column_REVISION)s from %(name)s
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [self._resourceID, ""]
+ )[0][0]
+ return "%s#%s" % (self._resourceID, revision,)
+ def objectResourcesSinceToken(self, token):
+ raise NotImplementedError()
+
def notificationObjectsSinceToken(self, token):
changed = []
removed = []
@@ -1025,6 +1074,123 @@
return (changed, removed, token)
+ def resourceNamesSinceToken(self, token):
+ results = [
+ (name.encode("utf-8"), deleted)
+ for name, deleted in
+ self._txn.execSQL("""
+ select %(column_RESOURCE_NAME)s, %(column_DELETED)s from %(name)s
+ where %(column_REVISION)s > %%s and %(column_HOME_RESOURCE_ID)s = %%s
+ """ % self._revisionsTable,
+ [token, self._resourceID],
+ )
+ ]
+ results.sort(key=lambda x:x[1])
+
+ changed = []
+ deleted = []
+ for name, wasdeleted in results:
+ if name:
+ if wasdeleted:
+ if token:
+ deleted.append(name)
+ else:
+ changed.append(name)
+
+ return changed, deleted,
+
+ def _updateSyncToken(self):
+
+ self._txn.execSQL("""
+ update %(name)s
+ set (%(column_REVISION)s) = (nextval('%(sequence)s'))
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [self._resourceID, ""]
+ )
+
+ def _insertRevision(self, name):
+ self._changeRevision("insert", name)
+
+ def _updateRevision(self, name):
+ self._changeRevision("update", name)
+
+ def _deleteRevision(self, name):
+ self._changeRevision("delete", name)
+
+ def _changeRevision(self, action, name):
+
+ nextrevision = self._txn.execSQL("""
+ select nextval('%(sequence)s')
+ """ % self._revisionsTable
+ )
+
+ if action == "delete":
+ self._txn.execSQL("""
+ update %(name)s
+ set (%(column_REVISION)s, %(column_DELETED)s) = (%%s, TRUE)
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [nextrevision, self._resourceID, name]
+ )
+ self._txn.execSQL("""
+ update %(name)s
+ set (%(column_REVISION)s) = (%%s)
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [nextrevision, self._resourceID, ""]
+ )
+ elif action == "update":
+ self._txn.execSQL("""
+ update %(name)s
+ set (%(column_REVISION)s) = (%%s)
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [nextrevision, self._resourceID, name]
+ )
+ self._txn.execSQL("""
+ update %(name)s
+ set (%(column_REVISION)s) = (%%s)
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [nextrevision, self._resourceID, ""]
+ )
+ elif action == "insert":
+ # Note that an "insert" may happen for a resource that previously existed and then
+ # was deleted. In that case an entry in the REVISIONS table still exists so we have to
+ # detect that and do db INSERT or UPDATE as appropriate
+
+ self._txn.execSQL("""
+ select %(column_HOME_RESOURCE_ID)s from %(name)s
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [self._resourceID, name, ]
+ )
+ found = self._txn._cursor.rowcount != 0
+ if found:
+ self._txn.execSQL("""
+ update %(name)s
+ set (%(column_REVISION)s, %(column_DELETED)s) = (%%s, FALSE)
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [nextrevision, self._resourceID, name]
+ )
+ else:
+ self._txn.execSQL("""
+ insert into %(name)s
+ (%(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_REVISION)s, %(column_DELETED)s)
+ values (%%s, %%s, %%s, FALSE)
+ """ % self._revisionsTable,
+ [self._resourceID, name, nextrevision]
+ )
+ self._txn.execSQL("""
+ update %(name)s
+ set (%(column_REVISION)s) = (%%s)
+ where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
+ """ % self._revisionsTable,
+ [nextrevision, self._resourceID, ""]
+ )
+
@cached
def properties(self):
return PropertyStore(
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql 2010-09-23 15:36:14 UTC (rev 6351)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql 2010-09-23 15:37:06 UTC (rev 6352)
@@ -211,27 +211,6 @@
);
-------------------------------
--- Calendar Object Revision --
-------------------------------
-
-create sequence CALENDAR_OBJECT_REVISION_SEQ;
-
-
--------------------------------
--- Calendar Object Revisions --
--------------------------------
-
-create table CALENDAR_OBJECT_REVISIONS (
- CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
- RESOURCE_NAME varchar(255) not null,
- REVISION integer not null,
- DELETED boolean not null,
-
- unique(CALENDAR_RESOURCE_ID, RESOURCE_NAME)
-);
-
-
------------------
-- iTIP Message --
------------------
@@ -318,24 +297,54 @@
unique(ADDRESSBOOK_RESOURCE_ID, VCARD_UID)
);
-------------------------------
--- AddressBook Object Revision --
-------------------------------
+---------------
+-- Revisions --
+---------------
-create sequence ADDRESSBOOK_OBJECT_REVISION_SEQ;
+create sequence REVISION_SEQ;
+---------------
+-- Revisions --
+---------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ REVISION integer not null,
+ DELETED boolean not null,
+
+ unique(CALENDAR_RESOURCE_ID, RESOURCE_NAME)
+);
+
+
-------------------------------
-- AddressBook Object Revisions --
-------------------------------
create table ADDRESSBOOK_OBJECT_REVISIONS (
- ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
- RESOURCE_NAME varchar(255) not null,
- REVISION integer not null,
- DELETED boolean not null,
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ REVISION integer not null,
+ DELETED boolean not null,
unique(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME)
);
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ REVISION integer not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME)
+);
+
+
Modified: CalendarServer/trunk/txdav/common/datastore/sql_tables.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_tables.py 2010-09-23 15:36:14 UTC (rev 6351)
+++ CalendarServer/trunk/txdav/common/datastore/sql_tables.py 2010-09-23 15:37:06 UTC (rev 6352)
@@ -77,7 +77,8 @@
CALENDAR_OBJECT_REVISIONS_TABLE = {
"name" : "CALENDAR_OBJECT_REVISIONS",
- "sequence" : "CALENDAR_OBJECT_REVISION_SEQ",
+ "sequence" : "REVISION_SEQ",
+ "column_HOME_RESOURCE_ID" : "CALENDAR_HOME_RESOURCE_ID",
"column_RESOURCE_ID" : "CALENDAR_RESOURCE_ID",
"column_RESOURCE_NAME" : "RESOURCE_NAME",
"column_REVISION" : "REVISION",
@@ -86,13 +87,23 @@
ADDRESSBOOK_OBJECT_REVISIONS_TABLE = {
"name" : "ADDRESSBOOK_OBJECT_REVISIONS",
- "sequence" : "ADDRESSBOOK_OBJECT_REVISION_SEQ",
+ "sequence" : "REVISION_SEQ",
+ "column_HOME_RESOURCE_ID" : "ADDRESSBOOK_HOME_RESOURCE_ID",
"column_RESOURCE_ID" : "ADDRESSBOOK_RESOURCE_ID",
"column_RESOURCE_NAME" : "RESOURCE_NAME",
"column_REVISION" : "REVISION",
"column_DELETED" : "DELETED",
}
+NOTIFICATION_OBJECT_REVISIONS_TABLE = {
+ "name" : "NOTIFICATION_OBJECT_REVISIONS",
+ "sequence" : "REVISION_SEQ",
+ "column_HOME_RESOURCE_ID" : "NOTIFICATION_HOME_RESOURCE_ID",
+ "column_RESOURCE_NAME" : "RESOURCE_NAME",
+ "column_REVISION" : "REVISION",
+ "column_DELETED" : "DELETED",
+}
+
CALENDAR_OBJECT_TABLE = {
"name" : "CALENDAR_OBJECT",
"column_RESOURCE_ID" : "RESOURCE_ID",
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100923/16725612/attachment-0001.html>
More information about the calendarserver-changes
mailing list