[CalendarServer-changes] [11460] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Wed Jul 3 19:29:51 PDT 2013
Revision: 11460
http://trac.calendarserver.org//changeset/11460
Author: cdaboo at apple.com
Date: 2013-07-03 19:29:51 -0700 (Wed, 03 Jul 2013)
Log Message:
-----------
Event splitter implementation that includes a work queue to split in the background. This also required
some changes to managed attachments to make splitting easier (plus some clean up of managed attachments
api). Still need to work on splitting for users not on the local server.
Modified Paths:
--------------
CalendarServer/trunk/support/build.sh
CalendarServer/trunk/twistedcaldav/ical.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py
CalendarServer/trunk/txdav/caldav/datastore/file.py
CalendarServer/trunk/txdav/caldav/datastore/scheduling/icalsplitter.py
CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
CalendarServer/trunk/txdav/caldav/icalendarstore.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
Added Paths:
-----------
CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql
CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql
CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql
CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql
Modified: CalendarServer/trunk/support/build.sh
===================================================================
--- CalendarServer/trunk/support/build.sh 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/support/build.sh 2013-07-04 02:29:51 UTC (rev 11460)
@@ -795,7 +795,7 @@
"${pypi}/p/${n}/${p}.tar.gz";
# XXX actually PyCalendar should be imported in-place.
- py_dependency -fe -i "src" -r 11390 \
+ py_dependency -fe -i "src" -r 11458 \
"PyCalendar" "pycalendar" "pycalendar" \
"${svn_uri_base}/PyCalendar/trunk";
Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/twistedcaldav/ical.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -1083,9 +1083,9 @@
@type rid: L{PyCalendarDateTime}
"""
+ if not self.isRecurring():
+ return
master = self.masterComponent()
- if not master.isRecurring():
- return
if master:
# Adjust any RRULE first
rrules = master._pycalendar.getRecurrenceSet()
@@ -1128,7 +1128,12 @@
self._markAsDirty()
+ # We changed the instance set so remove any instance cache
+ # TODO: we could be smarter here and truncate the instance list
+ if hasattr(self, "cachedInstances"):
+ delattr(self, "cachedInstances")
+
def onlyFutureInstances(self, rid):
"""
Remove all recurrence instances from the specified recurrence-id into the past. Adjust the bounds of
@@ -1140,9 +1145,9 @@
@type rid: L{PyCalendarDateTime}
"""
+ if not self.isRecurring():
+ return
master = self.masterComponent()
- if not master.isRecurring():
- return
if master:
# Check if cut-off matches an RDATE
adjusted_rid = rid
@@ -1221,7 +1226,12 @@
self._markAsDirty()
+ # We changed the instance set so remove any instance cache
+ # TODO: we could be smarter here and truncate the instance list
+ if hasattr(self, "cachedInstances"):
+ delattr(self, "cachedInstances")
+
def expand(self, start, end, timezone=None):
"""
Expand the components into a set of new components, one for each
@@ -1617,13 +1627,13 @@
return self._resource_uid
- def newUID(self):
+ def newUID(self, newUID=None):
"""
Generate a new UID for all components in this VCALENDAR
"""
assert self.name() == "VCALENDAR", "Not a calendar: %r" % (self,)
- newUID = str(uuid.uuid4())
+ newUID = str(uuid.uuid4()) if newUID is None else newUID
self._pycalendar.changeUID(self.resourceUID(), newUID)
self._resource_uid = newUID
self._markAsDirty()
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -730,6 +730,13 @@
# "decline-if-busy" - decline if busy, do nothing if free
# "automatic" - accept if free, decline if busy
"FutureFreeBusyDays" : 3 * 365, # How far into the future to check for booking conflicts
+ },
+
+ "Splitting": {
+ "Enabled" : True,
+ "Size" : 100 * 1024, # Consider splitting when greater than 100KB
+ "PastDays" : 14, # Number of days in the past where the split will occur
+ "Delay" : 60, # How many seconds to delay the split work item
}
}
},
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -2421,28 +2421,7 @@
returnValue(NO_CONTENT)
- @inlineCallbacks
- def preProcessManagedAttachments(self, calendar):
- # If store object exists pass through, otherwise use underlying store ManagedAttachments object to determine changes
- if self._newStoreObject:
- copied, removed = (yield self._newStoreObject.updatingResourceCheckAttachments(calendar))
- else:
- copied = (yield self._newStoreParent.creatingResourceCheckAttachments(calendar))
- removed = None
- returnValue((copied, removed,))
-
-
- @inlineCallbacks
- def postProcessManagedAttachments(self, copied, removed):
- # Pass through directly to store object
- if copied:
- yield self._newStoreObject.copyResourceAttachments(copied)
- if removed:
- yield self._newStoreObject.removeResourceAttachments(removed)
-
-
-
class _MetadataProperty(object):
"""
A python property which can be set either on a _newStoreObject or on some
Modified: CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/twistedcaldav/test/test_collectioncontents.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -50,7 +50,7 @@
# Need to not do implicit behavior during these tests
def _fakeDoImplicitScheduling(self, component, inserting, internal_state):
- return False, None, False
+ return False, None, False, None
self.patch(CalendarObject , "doImplicitScheduling",
_fakeDoImplicitScheduling)
Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/file.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -379,17 +379,7 @@
pass
- def creatingResourceCheckAttachments(self, component):
- """
- When component data is created or changed we need to look for changes related to managed attachments.
- @param component: the new calendar data
- @type component: L{Component}
- """
- return succeed(None)
-
-
-
class CalendarObject(CommonObjectResource, CalendarObjectBase):
"""
@ivar _path: The path of the .ics file on disk
Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/icalsplitter.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/icalsplitter.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/icalsplitter.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+
from pycalendar.datetime import PyCalendarDateTime
+
from twistedcaldav.ical import Property
@@ -61,35 +63,35 @@
now.offsetDay(1)
instances = ical.cacheExpandedTimeRanges(now)
instances = sorted(instances.instances.values(), key=lambda x: x.start)
- if instances[0].start > self.past or instances[-1].start < self.now or len(instances) <= 1:
+ if instances[0].start >= self.past or instances[-1].start < self.now or len(instances) <= 1:
return False
# Make sure there are some overridden components in the past - as splitting only makes sense when
# overrides are present
+ past_count = 0
for instance in instances:
- if instance.start > self.past:
- return False
+ if instance.start >= self.past:
+ break
elif instance.component.hasProperty("RECURRENCE-ID"):
- break
- else:
+ past_count += 1
+
+ # Only split when there is more than one past override to split off
+ if past_count < 2:
return False
# Now see if overall size exceeds our threshold
return len(str(ical)) > self.threshold
- def split(self, ical):
+ def whereSplit(self, ical):
"""
- Split the specified iCalendar object. This assumes that L{willSplit} has already
- been called and returned C{True}. Splitting is done by carving out old instances
- into a new L{Component} and adjusting the specified component for the on-going
- set. A RELATED-TO property is added to link old to new.
+ Determine where a split is going to happen - i.e., the RECURRENCE-ID.
- @param ical: the iCalendar object to split
+ @param ical: the iCalendar object to test
@type ical: L{Component}
- @return: iCalendar object for the old "carved out" instances
- @rtype: L{Component}
+ @return: recurrence-id of the split
+ @rtype: L{PyCalendarDateTime}
"""
# Find the instance RECURRENCE-ID where a split is going to happen
@@ -102,10 +104,41 @@
rid = instance.rid
if instance.start >= self.past:
break
+ else:
+ # We can get here when splitting and event for overrides only in the past,
+ # which happens when splitting an Attendee's copy of an Organizer event
+ # where the Organizer event has L{willSplit} == C{True}
+ rid = self.past
- # Create the old one with a new UID value
+ return rid
+
+
+ def split(self, ical, rid=None, newUID=None):
+ """
+ Split the specified iCalendar object. This assumes that L{willSplit} has already
+ been called and returned C{True}. Splitting is done by carving out old instances
+ into a new L{Component} and adjusting the specified component for the on-going
+ set. A RELATED-TO property is added to link old to new.
+
+ @param ical: the iCalendar object to split
+ @type ical: L{Component}
+
+ @param rid: recurrence-id where the split should occur, or C{None} to determine it here
+ @type rid: L{PyCalendarDateTime} or C{None}
+
+ @param newUID: UID to use for the split off component, or C{None} to generate one here
+ @type newUID: C{str} or C{None}
+
+ @return: iCalendar object for the old "carved out" instances
+ @rtype: L{Component}
+ """
+
+ # Find the instance RECURRENCE-ID where a split is going to happen
+ rid = self.whereSplit(ical) if rid is None else rid
+
+ # Create the old one with a new UID value (or the one passed in)
icalOld = ical.duplicate()
- oldUID = icalOld.newUID()
+ oldUID = icalOld.newUID(newUID=newUID)
icalOld.onlyPastInstances(rid)
# Adjust the current one
Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_icalsplitter.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -290,7 +290,7 @@
False,
),
(
- "#4.2 Large, old, recurring component with past override",
+ "#4.2 Large, old, recurring component with one past override",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -337,6 +337,65 @@
END:VEVENT
END:VCALENDAR
""",
+ False,
+ ),
+ (
+ "#4.2 Large, old, recurring component with two past overrides",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ATTENDEE:mailto:user3 at example.com
+ATTENDEE:mailto:user4 at example.com
+ATTENDEE:mailto:user5 at example.com
+ATTENDEE:mailto:user6 at example.com
+ATTENDEE:mailto:user7 at example.com
+ATTENDEE:mailto:user8 at example.com
+ATTENDEE:mailto:user9 at example.com
+ATTENDEE:mailto:user10 at example.com
+ATTENDEE:mailto:user11 at example.com
+ATTENDEE:mailto:user12 at example.com
+ATTENDEE:mailto:user13 at example.com
+ATTENDEE:mailto:user14 at example.com
+ATTENDEE:mailto:user15 at example.com
+ATTENDEE:mailto:user16 at example.com
+ATTENDEE:mailto:user17 at example.com
+ATTENDEE:mailto:user18 at example.com
+ATTENDEE:mailto:user19 at example.com
+ATTENDEE:mailto:user20 at example.com
+ATTENDEE:mailto:user21 at example.com
+ATTENDEE:mailto:user22 at example.com
+ATTENDEE:mailto:user23 at example.com
+ATTENDEE:mailto:user24 at example.com
+ATTENDEE:mailto:user25 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
True,
),
(
@@ -367,7 +426,7 @@
False,
),
(
- "#4.2 Large, old, limited recurring component with past override",
+ "#5.2 Large, old, limited recurring component with past override",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -444,7 +503,7 @@
False,
),
(
- "#6.2 Large, old, limited future recurring component with past override",
+ "#6.2 Large, old, limited future recurring component with two past overrides",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -489,6 +548,15 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE:mailto:user2 at example.com
END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ORGANIZER:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
END:VCALENDAR
""",
True,
@@ -552,6 +620,15 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER:mailto:user1 at example.com
END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
@@ -638,6 +715,16 @@
ORGANIZER:mailto:user1 at example.com
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
END:VCALENDAR
""",
),
@@ -693,6 +780,15 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER:mailto:user1 at example.com
END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
@@ -785,6 +881,16 @@
ORGANIZER:mailto:user1 at example.com
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
END:VCALENDAR
""",
),
@@ -835,6 +941,15 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER:mailto:user1 at example.com
END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
@@ -922,6 +1037,16 @@
ORGANIZER:mailto:user1 at example.com
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
END:VCALENDAR
""",
),
@@ -963,7 +1088,7 @@
RDATE:%(now_back15)s
RDATE:%(now_back14)s
RDATE:%(now_back13)s
-RRULE:FREQ=DAILY;INTERVAL=20
+RRULE:FREQ=DAILY;INTERVAL=10
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -976,6 +1101,15 @@
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
+RECURRENCE-ID:%(now_back20)s
+DTSTART:%(now_back20)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
RECURRENCE-ID:%(now_back10)s
DTSTART:%(now_back10)s
DURATION:PT1H
@@ -1021,7 +1155,7 @@
RDATE:%(now_back14)s
RDATE:%(now_back13)s
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
-RRULE:FREQ=DAILY;INTERVAL=20
+RRULE:FREQ=DAILY;INTERVAL=10
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1070,7 +1204,7 @@
ORGANIZER:mailto:user1 at example.com
RDATE:%(now_back15)s
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
-RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s;INTERVAL=20
+RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s;INTERVAL=10
END:VEVENT
BEGIN:VEVENT
UID:%(relID)s
@@ -1082,6 +1216,16 @@
ORGANIZER:mailto:user1 at example.com
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back20)s
+DTSTART:%(now_back20)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
END:VCALENDAR
""",
),
@@ -1138,6 +1282,15 @@
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
+RECURRENCE-ID:%(now_back15)s
+DTSTART:%(now_back15)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
RECURRENCE-ID:%(now_back14)s
DTSTART:%(now_back14)s
DURATION:PT1H
@@ -1263,6 +1416,16 @@
ORGANIZER:mailto:user1 at example.com
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back15)s
+DTSTART:%(now_back15)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
END:VCALENDAR
""",
),
@@ -1318,6 +1481,15 @@
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
+RECURRENCE-ID:%(now_back15)s
+DTSTART:%(now_back15)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
RECURRENCE-ID:%(now_fwd1)s
DTSTART:%(now_fwd1)s
DURATION:PT1H
@@ -1423,6 +1595,16 @@
ORGANIZER:mailto:user1 at example.com
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back15)s
+DTSTART:%(now_back15)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
END:VCALENDAR
""",
),
@@ -1472,6 +1654,15 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER:mailto:user1 at example.com
END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890
X-CALENDARSERVER-PERUSER-UID:user01
@@ -1609,6 +1800,16 @@
ORGANIZER:mailto:user1 at example.com
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:%(relID)s
X-CALENDARSERVER-PERUSER-UID:user01
@@ -1633,14 +1834,187 @@
END:VCALENDAR
""",
),
+ (
+ "2.1 - Overrides only - one future, one past",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+""",
+ ),
+ (
+ "2.2 - Overrides only - all past",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+""",
+ ),
+ (
+ "2.3 - Overrides only - all future",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd9)s
+DTSTART:%(now_fwd9)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd9)s
+DTSTART:%(now_fwd9)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+END:VCALENDAR
+""",
+ ),
)
for title, calendar, split_future, split_past in data:
ical = Component.fromString(calendar % self.subs)
splitter = iCalSplitter(1024, 14)
- self.assertTrue(splitter.willSplit(ical))
+ if title[0] == "1":
+ self.assertTrue(splitter.willSplit(ical), "Failed will split: %s" % (title,))
icalOld = splitter.split(ical)
relsubs = dict(self.subs)
relsubs["relID"] = icalOld.resourceUID()
self.assertEqual(str(ical).replace("\r\n ", ""), split_future.replace("\n", "\r\n") % relsubs, "Failed future: %s" % (title,))
self.assertEqual(str(icalOld).replace("\r\n ", ""), split_past.replace("\n", "\r\n") % relsubs, "Failed past: %s" % (title,))
+
+ # Make sure new items won't split again
+ self.assertFalse(splitter.willSplit(ical), "Failed future will split: %s" % (title,))
+ self.assertFalse(splitter.willSplit(icalOld), "Failed past will split: %s" % (title,))
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -26,6 +26,7 @@
"CalendarObject",
]
+from twext.enterprise.dal.record import fromTable
from twext.enterprise.dal.syntax import Delete
from twext.enterprise.dal.syntax import Insert
from twext.enterprise.dal.syntax import Len
@@ -34,6 +35,7 @@
from twext.enterprise.dal.syntax import Update
from twext.enterprise.dal.syntax import utcNowSQL
from twext.enterprise.locking import NamedLock
+from twext.enterprise.queue import WorkItem
from twext.enterprise.util import parseSQLTimestamp
from twext.python.clsprop import classproperty
from twext.python.filepath import CachingFilePath
@@ -45,7 +47,6 @@
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.python import hashlib
-
from twistedcaldav import caldavxml, customxml
from twistedcaldav.config import config
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
@@ -54,7 +55,9 @@
from twistedcaldav.ical import Component, InvalidICalendarDataError, Property
from twistedcaldav.instance import InvalidOverriddenInstanceError
from twistedcaldav.memcacher import Memcacher
+
from txdav.base.propertystore.base import PropertyName
+from txdav.caldav.datastore.scheduling.icalsplitter import iCalSplitter
from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
from txdav.caldav.datastore.util import AttachmentRetrievalTransport, \
normalizationLookup
@@ -95,6 +98,7 @@
from urlparse import urlparse, urlunparse
import collections
+import datetime
import os
import tempfile
import urllib
@@ -317,7 +321,75 @@
returnValue(results)
+ @inlineCallbacks
+ def calendarObjectsWithUID(self, txn, uid):
+ """
+ Return all child object resources with the specified UID. Only "owned" resources are returned,
+ no shared resources.
+ @param txn: transaction to use
+ @type txn: L{CommonStoreTransaction}
+ @param uid: UID to query
+ @type uid: C{str}
+
+ @return: list of matching L{CalendarObject}s
+ @rtype: C{list}
+ """
+
+ # First find the resource-ids of the (home, parent, object) for each object matching the UID.
+ obj = CalendarHome._objectSchema
+ bind = CalendarHome._bindSchema
+ rows = (yield Select(
+ [bind.HOME_RESOURCE_ID, obj.PARENT_RESOURCE_ID, obj.RESOURCE_ID],
+ From=obj.join(bind, obj.PARENT_RESOURCE_ID == bind.RESOURCE_ID),
+ Where=(obj.UID == Parameter("uid")).And(bind.BIND_MODE == _BIND_MODE_OWN),
+ ).on(txn, uid=uid))
+
+ results = []
+ for homeID, parentID, childID in rows:
+ home = (yield txn.calendarHomeWithResourceID(homeID))
+ parent = (yield home.childWithID(parentID))
+ child = (yield parent.objectResourceWithID(childID))
+ results.append(child)
+
+ returnValue(results)
+
+
+ @inlineCallbacks
+ def calendarObjectWithID(self, txn, rid):
+ """
+ Return all child object resources with the specified UID. Only "owned" resources are returned,
+ no shared resources.
+
+ @param txn: transaction to use
+ @type txn: L{CommonStoreTransaction}
+ @param rid: resource-id to query
+ @type rid: C{int}
+
+ @return: matching calendar object, or None if not found
+ @rtype: L{CalendarObject} or C{None}
+ """
+
+ # First find the resource-ids of the (home, parent, object) for each object matching the UID.
+ obj = CalendarHome._objectSchema
+ bind = CalendarHome._bindSchema
+ rows = (yield Select(
+ [bind.HOME_RESOURCE_ID, obj.PARENT_RESOURCE_ID, obj.RESOURCE_ID],
+ From=obj.join(bind, obj.PARENT_RESOURCE_ID == bind.RESOURCE_ID),
+ Where=(obj.RESOURCE_ID == Parameter("rid")).And(bind.BIND_MODE == _BIND_MODE_OWN),
+ ).on(txn, rid=rid))
+
+ results = []
+ for homeID, parentID, childID in rows:
+ home = (yield txn.calendarHomeWithResourceID(homeID))
+ parent = (yield home.childWithID(parentID))
+ child = (yield parent.objectResourceWithID(childID))
+ results.append(child)
+
+ returnValue(results[0] if results else None)
+
+
+
class CalendarHome(CommonHome):
implements(ICalendarHome)
@@ -545,12 +617,6 @@
@inlineCallbacks
- def attachmentObjectWithID(self, managedID):
- attach = (yield ManagedAttachment.load(self._txn, managedID))
- returnValue(attach)
-
-
- @inlineCallbacks
def createdHome(self):
# Default calendar
@@ -1371,16 +1437,6 @@
)
- def creatingResourceCheckAttachments(self, component):
- """
- When component data is created or changed we need to look for changes related to managed attachments.
-
- @param component: the new calendar data
- @type component: L{Component}
- """
- return CalendarObject.creatingResourceCheckAttachments(self._txn, self, component)
-
-
icalfbtype_to_indexfbtype = {
"UNKNOWN" : 0,
"FREE" : 1,
@@ -1879,6 +1935,8 @@
new_component = None
did_implicit_action = False
+ is_scheduling_resource = False
+ schedule_state = None
is_internal = internal_state not in (ComponentUpdateState.NORMAL, ComponentUpdateState.ATTACHMENT_UPDATE,)
@@ -1910,10 +1968,9 @@
else:
new_component = new_calendar
did_implicit_action = True
- else:
- is_scheduling_resource = False
+ schedule_state = scheduler.state
- returnValue((is_scheduling_resource, new_component, did_implicit_action,))
+ returnValue((is_scheduling_resource, new_component, did_implicit_action, schedule_state,))
@inlineCallbacks
@@ -2028,8 +2085,26 @@
self._componentChanged = False
self.schedule_tag_match = not self.calendar().isInbox() and internal_state == ComponentUpdateState.NORMAL and smart_merge
+ schedule_state = None
- if internal_state != ComponentUpdateState.RAW:
+ if internal_state == ComponentUpdateState.SPLIT:
+ # When splitting, some state from the previous resource needs to be properly
+ # preserved in thus new one when storing the component. Since we don't do the "full"
+ # store here, we need to add the explicit pieces we need for state preservation.
+
+ # Check access
+ if config.EnablePrivateEvents:
+ self.validAccess(component, inserting, internal_state)
+
+ # Preserve private comments
+ yield self.preservePrivateComments(component, inserting)
+
+ managed_copied, managed_removed = (yield self.resourceCheckAttachments(component, inserting))
+
+ self.isScheduleObject = True
+ self.processScheduleTags(component, inserting, internal_state)
+
+ elif internal_state != ComponentUpdateState.RAW:
# Handle all validation operations here.
yield self.fullValidation(component, inserting, internal_state)
@@ -2047,7 +2122,7 @@
# Pre-process managed attachments
if internal_state == ComponentUpdateState.NORMAL:
- managed_copied, managed_removed = (yield self.updatingResourceCheckAttachments(component, inserting))
+ managed_copied, managed_removed = (yield self.resourceCheckAttachments(component, inserting))
# Default/duplicate alarms
self.processAlarms(component, inserting)
@@ -2072,7 +2147,7 @@
log.error(msg)
raise InvalidObjectResourceError(msg)
else:
- self.isScheduleObject, new_component, did_implicit_action = implicit_result
+ self.isScheduleObject, new_component, did_implicit_action, schedule_state = implicit_result
if new_component is not None:
component = new_component
if did_implicit_action:
@@ -2090,7 +2165,7 @@
yield self.updateDatabase(component, inserting=inserting)
# Post process managed attachments
- if internal_state == ComponentUpdateState.NORMAL:
+ if internal_state in (ComponentUpdateState.NORMAL, ComponentUpdateState.SPLIT):
if managed_copied:
yield self.copyResourceAttachments(managed_copied)
if managed_removed:
@@ -2103,6 +2178,10 @@
yield self._calendar.notifyChanged()
+ # Finally check if a split is needed
+ if internal_state != ComponentUpdateState.SPLIT and schedule_state == "organizer":
+ yield self.checkSplit()
+
returnValue(self._componentChanged)
@@ -2662,59 +2741,15 @@
hasPrivateComment = property(_get_hasPrivateComment, _set_hasPrivateComment)
@inlineCallbacks
- def _preProcessAttachmentsOnResourceChange(self, component, inserting):
+ def resourceCheckAttachments(self, component, inserting=False):
"""
- When component data is created or changed we need to look for changes related to managed attachments.
+ A component is being changed or added. Check any ATTACH properties that may be present
+ to verify they are owned by the organizer/owner of the resource. Strip any that are invalid.
- @param component: the new calendar data
- @type component: L{Component}
- @param inserting: C{True} if resource is being created
- @type inserting: C{bool}
- """
- if inserting:
- self._copyAttachments = (yield self.creatingResourceCheckAttachments(component))
- self._removeAttachments = None
- else:
- self._copyAttachments, self._removeAttachments = (yield self.updatingResourceCheckAttachments(component))
-
-
- @classmethod
- @inlineCallbacks
- def creatingResourceCheckAttachments(cls, txn, parent, component):
- """
- A new component is going to be stored. Check any ATTACH properties that may be present
- to verify they are owned by the organizer/owner of the resource and re-write the managed-ids.
-
@param component: calendar component about to be stored
@type component: L{Component}
"""
- # Retrieve all ATTACH properties with a MANAGED-ID
- attached = collections.defaultdict(list)
- attachments = component.getAllPropertiesInAnyComponent("ATTACH", depth=1,)
- for attachment in attachments:
- managed_id = attachment.parameterValue("MANAGED-ID")
- if managed_id is not None:
- attached[managed_id].append(attachment)
-
- # Punt if no managed attachments
- if len(attached) == 0:
- returnValue(None)
-
- changes = yield cls._addingManagedIDs(txn, parent, str(uuid.uuid4()), attached, component.resourceUID())
- returnValue(changes)
-
-
- @inlineCallbacks
- def updatingResourceCheckAttachments(self, component, inserting=False):
- """
- A component is being changed. Check any ATTACH properties that may be present
- to verify they are owned by the organizer/owner of the resource and re-write the managed-ids.
-
- @param component: calendar component about to be stored
- @type component: L{Component}
- """
-
# Retrieve all ATTACH properties with a MANAGED-ID in new data
newattached = collections.defaultdict(list)
newattachments = component.getAllPropertiesInAnyComponent("ATTACH", depth=1,)
@@ -2762,7 +2797,6 @@
oldattachment = oldattached[managed_id][0]
for newattachment in newattached[managed_id]:
if newattachment != oldattachment:
- newattachment.setParameter("MTAG", oldattachment.parameterValue("MTAG"))
newattachment.setParameter("FMTTYPE", oldattachment.parameterValue("FMTTYPE"))
newattachment.setParameter("FILENAME", oldattachment.parameterValue("FILENAME"))
newattachment.setParameter("SIZE", oldattachment.parameterValue("SIZE"))
@@ -2771,9 +2805,8 @@
returnValue((changes, removed,))
- @classmethod
@inlineCallbacks
- def _addingManagedIDs(cls, txn, parent, dropbox_id, attached, newuid):
+ def _addingManagedIDs(self, txn, parent, dropbox_id, attached, newuid):
# Now check each managed-id
changes = []
for managed_id, attachments in attached.items():
@@ -2782,38 +2815,40 @@
details = (yield ManagedAttachment.usedManagedID(txn, managed_id))
if len(details) == 0:
raise AttachmentStoreValidManagedID
- if len(details) != 1:
+ homes = set()
+ uids = set()
+ for home_id, _ignore_resource_id, uid in details:
+ homes.add(home_id)
+ uids.add(uid)
+ if len(homes) != 1:
# This is a bad store error - there should be only one home associated with a managed-id
raise InternalDataStoreError
- home_id, _ignore_resource_id, uid = details[0]
# Policy:
#
# 1. If Managed-ID is re-used in a resource with the same UID - it is fine - just rewrite the details
- # 2. If Managed-ID is re-used in a different resource but owned by the same user - change managed-id to new one
+ # 2. If Managed-ID is re-used in a different resource but owned by the same user - just rewrite the details
# 3. Otherwise, strip off the managed-id property and treat as unmanaged.
# 1. UID check
- if uid == newuid:
- yield cls._syncAttachmentProperty(txn, managed_id, dropbox_id, attachments)
+ if newuid in uids:
+ yield self._syncAttachmentProperty(txn, managed_id, dropbox_id, attachments)
# 2. Same home
elif home_id == parent.ownerHome()._resourceID:
- # Need to rewrite the managed-id, value in the properties
- new_id = str(uuid.uuid4())
- yield cls._syncAttachmentProperty(txn, managed_id, dropbox_id, attachments, new_id)
- changes.append((managed_id, new_id,))
+ # Record the change as we will need to add a new reference to this attachment
+ yield self._syncAttachmentProperty(txn, managed_id, dropbox_id, attachments)
+ changes.append(managed_id)
else:
- cls._stripAttachmentProperty(attachments)
+ self._stripAttachmentProperty(attachments)
returnValue(changes)
- @classmethod
@inlineCallbacks
- def _syncAttachmentProperty(cls, txn, managed_id, dropbox_id, attachments, new_id=None):
+ def _syncAttachmentProperty(self, txn, managed_id, dropbox_id, attachments):
"""
Make sure the supplied set of attach properties are all sync'd with the current value of the
matching managed-id attachment.
@@ -2822,19 +2857,19 @@
@type managed_id: C{str}
@param attachments: list of attachment properties
@type attachments: C{list} of L{twistedcaldav.ical.Property}
- @param new_id: Value of new Managed-ID to use
- @type new_id: C{str}
"""
- new_attachment = (yield ManagedAttachment.load(txn, managed_id))
- if new_id:
- new_attachment._managedID = new_id
+
+ # Try to load any attachment directly referenced by this object, otherwise load one referenced by some
+ # any other - they are all the same.
+ new_attachment = (yield ManagedAttachment.load(txn, self._resourceID, managed_id))
+ if new_attachment is None:
+ new_attachment = (yield ManagedAttachment.load(txn, None, managed_id))
new_attachment._objectDropboxID = dropbox_id
for attachment in attachments:
yield new_attachment.updateProperty(attachment)
- @classmethod
- def _stripAttachmentProperty(cls, attachments):
+ def _stripAttachmentProperty(self, attachments):
"""
Strip off managed-id related properties from an attachment.
"""
@@ -2848,11 +2883,11 @@
"""
Copy an attachment reference for some other resource and link it to this resource.
- @param attached: tuple of old, new managed ids for the attachments to copy
- @type attached: C{tuple}
+ @param attached: managed id for the attachments to copy
+ @type attached: C{str}
"""
- for old_id, new_id in attached:
- yield ManagedAttachment.copyManagedID(self._txn, old_id, new_id, self._resourceID)
+ for managed_id in attached:
+ yield ManagedAttachment.copyManagedID(self._txn, managed_id, self._resourceID)
@inlineCallbacks
@@ -3102,13 +3137,13 @@
def attachmentWithManagedID(self, managed_id):
- return ManagedAttachment.load(self._txn, managed_id)
+ return ManagedAttachment.load(self._txn, self._resourceID, managed_id)
@inlineCallbacks
def removeManagedAttachmentWithID(self, managed_id):
attachment = (yield self.attachmentWithManagedID(managed_id))
- if attachment._objectResourceID == self._resourceID:
+ if attachment is not None:
yield attachment.removeFromResource(self._resourceID)
@@ -3218,7 +3253,116 @@
return MimeType.fromString("text/calendar; charset=utf-8")
+ @inlineCallbacks
+ def checkSplit(self):
+ """
+ Determine if the calendar data needs to be split, and enqueue a split work item if needed.
+ """
+ if config.Scheduling.Options.Splitting.Enabled:
+ will = (yield self.willSplit())
+ if will:
+ notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=config.Scheduling.Options.Splitting.Delay)
+ work = (yield self._txn.enqueue(CalendarObject.CalendarObjectSplitterWork, resourceID=self._resourceID, notBefore=notBefore))
+ if not hasattr(self, "_workItems"):
+ self._workItems = []
+ self._workItems.append(work)
+
+
+ @inlineCallbacks
+ def willSplit(self):
+ """
+ Determine if the calendar data needs to be split as per L{iCalSplitter}.
+ """
+
+ splitter = iCalSplitter(config.Scheduling.Options.Splitting.Size, config.Scheduling.Options.Splitting.PastDays)
+ ical = (yield self.component())
+ will_split = splitter.willSplit(ical)
+ returnValue(will_split)
+
+
+ @inlineCallbacks
+ def split(self):
+ """
+ Split this and all matching UID calendar objects as per L{iCalSplitter}.
+ """
+
+ # First job is to grab a UID lock on this entire series of events
+ yield NamedLock.acquire(self._txn, "ImplicitUIDLock:%s" % (hashlib.md5(self._uid).hexdigest(),))
+
+ # Find all other calendar objects on this server with the same UID
+ resources = (yield CalendarStoreFeatures(self._txn._store).calendarObjectsWithUID(self._txn, self._uid))
+
+ splitter = iCalSplitter(config.Scheduling.Options.Splitting.Size, config.Scheduling.Options.Splitting.PastDays)
+
+ # Determine the recurrence-id of the split and create a new UID for it
+ calendar = (yield self.component())
+ rid = splitter.whereSplit(calendar)
+ newUID = str(uuid.uuid4())
+
+ # Now process this resource, but do implicit scheduling for attendees not hosted on this server.
+ # We need to do this before processing attendee copies.
+ calendar_old = splitter.split(calendar, rid=rid, newUID=newUID)
+
+ # Store changed data
+ if calendar.mainType() is not None:
+ yield self._setComponentInternal(calendar, internal_state=ComponentUpdateState.SPLIT)
+ else:
+ yield self._removeInternal(internal_state=ComponentUpdateState.SPLIT)
+ if calendar_old.mainType() is not None:
+ yield self.calendar()._createCalendarObjectWithNameInternal("%s.ics" % (newUID,), calendar_old, ComponentUpdateState.SPLIT)
+
+ # Split each one - but not this resource
+ for resource in resources:
+ if resource._resourceID == self._resourceID:
+ continue
+ ical = (yield resource.component())
+ ical_old = splitter.split(ical, rid=rid, newUID=newUID)
+
+ # Store changed data
+ if ical.mainType() is not None:
+ yield resource._setComponentInternal(ical, internal_state=ComponentUpdateState.SPLIT)
+ else:
+ # The split removed all components from this object - remove it
+ yield resource._removeInternal(internal_state=ComponentUpdateState.SPLIT)
+
+ # Create a new resource and store its data (but not if the parent is "inbox", or if it is empty)
+ if not resource.calendar().isInbox() and ical_old.mainType() is not None:
+ yield resource.calendar()._createCalendarObjectWithNameInternal("%s.ics" % (newUID,), ical_old, ComponentUpdateState.SPLIT)
+
+ # TODO: scheduling currently turned off until we figure out how to properly do that
+
+ returnValue(newUID)
+
+
+ class CalendarObjectSplitterWork(WorkItem, fromTable(schema.CALENDAR_OBJECT_SPLITTER_WORK)):
+
+ group = property(lambda self: "CalendarObjectSplitterWork:%s" % (self.resourceID,))
+
+ @inlineCallbacks
+ def doWork(self):
+
+ log.debug("Splitting calendar object with resource-id: {rid}", rid=self.resourceID)
+
+ # Delete all other work items with the same resourceID
+ yield Delete(From=self.table,
+ Where=self.table.RESOURCE_ID == self.resourceID
+ ).on(self.transaction)
+
+ # Get the actual owned calendar object with this ID
+ cobj = (yield CalendarStoreFeatures(self.transaction._store).calendarObjectWithID(self.transaction, self.resourceID))
+ if cobj is None:
+ returnValue(None)
+
+ # Check it is still split-able
+ will = (yield cobj.willSplit())
+
+ if will:
+ # Now do the spitting
+ yield cobj.split()
+
+
+
class AttachmentStorageTransport(StorageTransportBase):
_TEMPORARY_UPLOADS_DIRECTORY = "Temporary"
@@ -3679,7 +3823,8 @@
# Create an "orphaned" ManagedAttachment that points to the updated data but without
# an actual managed-id (which only exists when there is a reference to a calendar object).
- mattach = (yield ManagedAttachment.load(self._txn, None, attachmentID=self._attachmentID))
+ mattach = (yield ManagedAttachment.load(self._txn, None, None, attachmentID=self._attachmentID))
+ mattach._managedID = str(uuid.uuid4())
if mattach is None:
raise AttachmentMigrationFailed
@@ -3765,30 +3910,29 @@
# Now create the DB entry
attachment = (yield cls._create(txn, managedID, ownerHomeID))
+ attachment._objectResourceID = referencedBy
# Create the attachment<->calendar object relationship for managed attachments
attco = schema.ATTACHMENT_CALENDAR_OBJECT
yield Insert({
attco.ATTACHMENT_ID : attachment._attachmentID,
- attco.MANAGED_ID : managedID,
- attco.CALENDAR_OBJECT_RESOURCE_ID : referencedBy,
+ attco.MANAGED_ID : attachment._managedID,
+ attco.CALENDAR_OBJECT_RESOURCE_ID : attachment._objectResourceID,
}).on(txn)
- attachment._managedID = managedID
- attachment._objectResourceID = referencedBy
returnValue(attachment)
@classmethod
@inlineCallbacks
- def update(cls, txn, managedID, ownerHomeID, referencedBy, oldAttachmentID):
+ def update(cls, txn, oldManagedID, ownerHomeID, referencedBy, oldAttachmentID):
"""
Create a new Attachment object.
@param txn: The transaction to use
@type txn: L{CommonStoreTransaction}
- @param managedID: the identifier for the attachment
- @type managedID: C{str}
+ @param oldManagedID: the identifier for the original attachment
+ @type oldManagedID: C{str}
@param ownerHomeID: the resource-id of the home collection of the attachment owner
@type ownerHomeID: C{int}
@param referencedBy: the resource-id of the calendar object referencing the attachment
@@ -3797,21 +3941,22 @@
@type oldAttachmentID: C{int}
"""
- # Now create the DB entry
- attachment = (yield cls._create(txn, managedID, ownerHomeID))
+ # Now create the DB entry with a new managed-ID
+ managed_id = str(uuid.uuid4())
+ attachment = (yield cls._create(txn, managed_id, ownerHomeID))
+ attachment._objectResourceID = referencedBy
# Update the attachment<->calendar object relationship for managed attachments
attco = schema.ATTACHMENT_CALENDAR_OBJECT
yield Update(
{
attco.ATTACHMENT_ID : attachment._attachmentID,
+ attco.MANAGED_ID : attachment._managedID,
},
- Where=(attco.MANAGED_ID == managedID).And(
- attco.CALENDAR_OBJECT_RESOURCE_ID == referencedBy
+ Where=(attco.MANAGED_ID == oldManagedID).And(
+ attco.CALENDAR_OBJECT_RESOURCE_ID == attachment._objectResourceID
),
).on(txn)
- attachment._managedID = managedID
- attachment._objectResourceID = referencedBy
# Now check whether old attachmentID is still referenced - if not delete it
rows = (yield Select(
@@ -3830,30 +3975,31 @@
@classmethod
@inlineCallbacks
- def load(cls, txn, managedID, attachmentID=None):
+ def load(cls, txn, referencedID, managedID, attachmentID=None):
"""
- Create a ManagedAttachment via either its managedID or attachmentID.
+ Load a ManagedAttachment via either its managedID or attachmentID.
"""
if managedID:
attco = schema.ATTACHMENT_CALENDAR_OBJECT
+ where = (attco.MANAGED_ID == managedID)
+ if referencedID is not None:
+ where = where.And(attco.CALENDAR_OBJECT_RESOURCE_ID == referencedID)
rows = (yield Select(
- [attco.ATTACHMENT_ID, attco.CALENDAR_OBJECT_RESOURCE_ID, ],
+ [attco.ATTACHMENT_ID, ],
From=attco,
- Where=(attco.MANAGED_ID == managedID),
+ Where=where,
).on(txn))
if len(rows) == 0:
returnValue(None)
- elif len(rows) != 1:
+ elif referencedID is not None and len(rows) != 1:
raise AttachmentStoreValidManagedID
- rows = rows[0]
- else:
- rows = (attachmentID, None,)
+ attachmentID = rows[0][0]
- attachment = cls(txn, rows[0], None, None)
+ attachment = cls(txn, attachmentID, None, None)
attachment = (yield attachment.initFromStore())
attachment._managedID = managedID
- attachment._objectResourceID = rows[1]
+ attachment._objectResourceID = referencedID
returnValue(attachment)
@@ -3912,29 +4058,28 @@
).on(txn))
mids = set([row[0] for row in rows]) if rows is not None else set()
for managedID in mids:
- attachment = (yield ManagedAttachment.load(txn, managedID))
+ attachment = (yield ManagedAttachment.load(txn, resourceID, managedID))
(yield attachment.removeFromResource(resourceID))
@classmethod
@inlineCallbacks
- def copyManagedID(cls, txn, oldManagedID, newManagedID, referencedBy):
+ def copyManagedID(cls, txn, managedID, referencedBy):
"""
- Copy a managed-ID to a new ID and associate the original attachment with the
- new resource.
+ Associate an existing attachment with the new resource.
"""
- # Find all reference attachment-ids and dereference
+ # Find the associated attachment-id and insert new reference
attco = schema.ATTACHMENT_CALENDAR_OBJECT
aid = (yield Select(
[attco.ATTACHMENT_ID, ],
From=attco,
- Where=(attco.MANAGED_ID == oldManagedID),
+ Where=(attco.MANAGED_ID == managedID),
).on(txn))[0][0]
yield Insert({
attco.ATTACHMENT_ID : aid,
- attco.MANAGED_ID : newManagedID,
+ attco.MANAGED_ID : managedID,
attco.CALENDAR_OBJECT_RESOURCE_ID : referencedBy,
}).on(txn)
@@ -4020,7 +4165,7 @@
@inlineCallbacks
def newReference(self, resourceID):
"""
- Create a new managed-id that references the supplied calendar object resource id, and
+ Create a new reference of this attachment to the supplied calendar object resource id, and
return a ManagedAttachment for the new reference.
@param resourceID: the resource id to reference
@@ -4030,15 +4175,14 @@
@rtype: L{ManagedAttachment}
"""
- managed_id = str(uuid.uuid4())
attco = schema.ATTACHMENT_CALENDAR_OBJECT
yield Insert({
attco.ATTACHMENT_ID : self._attachmentID,
- attco.MANAGED_ID : managed_id,
+ attco.MANAGED_ID : self._managedID,
attco.CALENDAR_OBJECT_RESOURCE_ID : resourceID,
}).on(self._txn)
- mattach = (yield ManagedAttachment.load(self._txn, managed_id))
+ mattach = (yield ManagedAttachment.load(self._txn, resourceID, self._managedID))
returnValue(mattach)
@@ -4082,7 +4226,6 @@
location = (yield self.location())
attach.setParameter("MANAGED-ID", self.managedID())
- attach.setParameter("MTAG", self.md5())
attach.setParameter("FMTTYPE", "%s/%s" % (self.contentType().mediaType, self.contentType().mediaSubtype))
attach.setParameter("FILENAME", self.name())
attach.setParameter("SIZE", str(self.size()))
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -1602,7 +1602,7 @@
# Managed attachment present
txn = self._sqlCalendarStore.newTransaction()
- mattachment2 = (yield ManagedAttachment.load(txn, None, attachmentID=dattachment._attachmentID))
+ mattachment2 = (yield ManagedAttachment.load(txn, None, None, attachmentID=dattachment._attachmentID))
self.assertNotEqual(mattachment2, None)
self.assertTrue(mattachment2.isManaged())
@@ -1626,21 +1626,21 @@
self.assertTrue(dattachment._path.exists())
mattachment = (yield dattachment.convertToManaged())
self.assertNotEqual(mattachment, None)
- self.assertEqual(mattachment.managedID(), None)
+ self.assertNotEqual(mattachment.managedID(), None)
mnew4 = (yield mattachment.newReference(event4._resourceID))
self.assertNotEqual(mnew4, None)
- self.assertNotEqual(mnew4.managedID(), None)
+ self.assertEqual(mnew4.managedID(), mattachment.managedID())
mnew5 = (yield mattachment.newReference(event5._resourceID))
self.assertNotEqual(mnew5, None)
- self.assertNotEqual(mnew5.managedID(), None)
+ self.assertEqual(mnew5.managedID(), mattachment.managedID())
yield txn.commit()
# Managed attachment present
txn = self._sqlCalendarStore.newTransaction()
- mtest4 = (yield ManagedAttachment.load(txn, mnew4.managedID()))
+ mtest4 = (yield ManagedAttachment.load(txn, event4._resourceID, mnew4.managedID()))
self.assertNotEqual(mtest4, None)
self.assertTrue(mtest4.isManaged())
self.assertEqual(mtest4._objectResourceID, event4._resourceID)
@@ -1648,7 +1648,7 @@
# Managed attachment present
txn = self._sqlCalendarStore.newTransaction()
- mtest5 = (yield ManagedAttachment.load(txn, mnew5.managedID()))
+ mtest5 = (yield ManagedAttachment.load(txn, event5._resourceID, mnew5.managedID()))
self.assertNotEqual(mtest5, None)
self.assertTrue(mtest5.isManaged())
self.assertEqual(mtest5._objectResourceID, event5._resourceID)
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -24,6 +24,8 @@
from twext.enterprise.dal.syntax import Select, Parameter, Insert, Delete
from twext.python.vcomponent import VComponent
+from twext.web2.http_headers import MimeType
+from twext.web2.stream import MemoryStream
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue, DeferredList
@@ -48,7 +50,8 @@
from txdav.common.datastore.sql_legacy import PostgresLegacyIndexEmulator
from txdav.common.datastore.sql_tables import schema, _BIND_MODE_DIRECT, \
_BIND_STATUS_ACCEPTED, _BIND_MODE_WRITE, _BIND_STATUS_INVITED
-from txdav.common.datastore.test.util import populateCalendarsFrom
+from txdav.common.datastore.test.util import populateCalendarsFrom, \
+ CommonCommonTests
from txdav.common.icommondatastore import NoSuchObjectResourceError
from txdav.xml.rfc2518 import GETContentLanguage, ResourceType
@@ -1936,3 +1939,1550 @@
self.assertNotEqual(caldata2, None)
self.assertNotEqual(caldata3, None)
self.assertNotEqual(caldata4, None)
+
+
+
+class CalendarObjectSplitting(CommonCommonTests, unittest.TestCase):
+ """
+ CalendarObject splitting tests
+ """
+
+ @inlineCallbacks
+ def setUp(self):
+ yield super(CalendarObjectSplitting, self).setUp()
+ self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+
+ # Make sure homes are provisioned
+ txn = self.transactionUnderTest()
+ for ctr in range(1, 5):
+ home_uid = yield txn.homeWithUID(ECALENDARTYPE, "user%02d" % (ctr,), create=True)
+ self.assertNotEqual(home_uid, None)
+ yield self.commit()
+
+ self.subs = {}
+
+ self.now = PyCalendarDateTime.getNowUTC()
+ self.now.setHHMMSS(0, 0, 0)
+
+ self.subs["now"] = self.now
+
+ for i in range(30):
+ attrname = "now_back%s" % (i + 1,)
+ setattr(self, attrname, self.now.duplicate())
+ getattr(self, attrname).offsetDay(-(i + 1))
+ self.subs[attrname] = getattr(self, attrname)
+
+ attrname_12h = "now_back%s_12h" % (i + 1,)
+ setattr(self, attrname_12h, getattr(self, attrname).duplicate())
+ getattr(self, attrname_12h).offsetHours(12)
+ self.subs[attrname_12h] = getattr(self, attrname_12h)
+
+ attrname_1 = "now_back%s_1" % (i + 1,)
+ setattr(self, attrname_1, getattr(self, attrname).duplicate())
+ getattr(self, attrname_1).offsetSeconds(-1)
+ self.subs[attrname_1] = getattr(self, attrname_1)
+
+ for i in range(30):
+ attrname = "now_fwd%s" % (i + 1,)
+ setattr(self, attrname, self.now.duplicate())
+ getattr(self, attrname).offsetDay(i + 1)
+ self.subs[attrname] = getattr(self, attrname)
+
+ attrname_12h = "now_fwd%s_12h" % (i + 1,)
+ setattr(self, attrname_12h, getattr(self, attrname).duplicate())
+ getattr(self, attrname_12h).offsetHours(12)
+ self.subs[attrname_12h] = getattr(self, attrname_12h)
+
+ self.patch(config, "MaxAllowedInstances", 500)
+
+
+ @inlineCallbacks
+ def populate(self):
+ yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
+ self.notifierFactory.reset()
+
+
+ def storeUnderTest(self):
+ """
+ Create and return a L{CalendarStore} for testing.
+ """
+ return self._sqlCalendarStore
+
+
+ @inlineCallbacks
+ def test_calendarObjectSplit(self):
+ """
+ Test that (manual) splitting of calendar objects works.
+ """
+
+ self.patch(config.Scheduling.Options.Splitting, "Enabled", False)
+ self.patch(config.Scheduling.Options.Splitting, "Size", 1024)
+ self.patch(config.Scheduling.Options.Splitting, "PastDays", 14)
+
+ # Create one event that will split
+ calendar = yield self.calendarUnderTest(name="calendar", home="user01")
+
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.org
+ATTENDEE:mailto:user2 at example.org
+ATTENDEE:mailto:user3 at example.org
+ATTENDEE:mailto:user4 at example.org
+ATTENDEE:mailto:user5 at example.org
+ATTENDEE:mailto:user6 at example.org
+ATTENDEE:mailto:user7 at example.org
+ATTENDEE:mailto:user8 at example.org
+ATTENDEE:mailto:user9 at example.org
+ATTENDEE:mailto:user10 at example.org
+ATTENDEE:mailto:user11 at example.org
+ATTENDEE:mailto:user12 at example.org
+ATTENDEE:mailto:user13 at example.org
+ATTENDEE:mailto:user14 at example.org
+ATTENDEE:mailto:user15 at example.org
+ATTENDEE:mailto:user16 at example.org
+ATTENDEE:mailto:user17 at example.org
+ATTENDEE:mailto:user18 at example.org
+ATTENDEE:mailto:user19 at example.org
+ATTENDEE:mailto:user20 at example.org
+ATTENDEE:mailto:user21 at example.org
+ATTENDEE:mailto:user22 at example.org
+ATTENDEE:mailto:user23 at example.org
+ATTENDEE:mailto:user24 at example.org
+ATTENDEE:mailto:user25 at example.org
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user1 at example.org
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.org
+ATTENDEE:mailto:user2 at example.org
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user1 at example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.org
+ATTENDEE:mailto:user2 at example.org
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user1 at example.org
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_future = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.org
+ATTENDEE:mailto:user2 at example.org
+ATTENDEE:mailto:user3 at example.org
+ATTENDEE:mailto:user4 at example.org
+ATTENDEE:mailto:user5 at example.org
+ATTENDEE:mailto:user6 at example.org
+ATTENDEE:mailto:user7 at example.org
+ATTENDEE:mailto:user8 at example.org
+ATTENDEE:mailto:user9 at example.org
+ATTENDEE:mailto:user10 at example.org
+ATTENDEE:mailto:user11 at example.org
+ATTENDEE:mailto:user12 at example.org
+ATTENDEE:mailto:user13 at example.org
+ATTENDEE:mailto:user14 at example.org
+ATTENDEE:mailto:user15 at example.org
+ATTENDEE:mailto:user16 at example.org
+ATTENDEE:mailto:user17 at example.org
+ATTENDEE:mailto:user18 at example.org
+ATTENDEE:mailto:user19 at example.org
+ATTENDEE:mailto:user20 at example.org
+ATTENDEE:mailto:user21 at example.org
+ATTENDEE:mailto:user22 at example.org
+ATTENDEE:mailto:user23 at example.org
+ATTENDEE:mailto:user24 at example.org
+ATTENDEE:mailto:user25 at example.org
+DTSTAMP:20051222T210507Z
+ORGANIZER;SCHEDULE-AGENT=NONE;SCHEDULE-STATUS=5.3:mailto:user1 at example.org
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_past = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.org
+ATTENDEE:mailto:user2 at example.org
+ATTENDEE:mailto:user3 at example.org
+ATTENDEE:mailto:user4 at example.org
+ATTENDEE:mailto:user5 at example.org
+ATTENDEE:mailto:user6 at example.org
+ATTENDEE:mailto:user7 at example.org
+ATTENDEE:mailto:user8 at example.org
+ATTENDEE:mailto:user9 at example.org
+ATTENDEE:mailto:user10 at example.org
+ATTENDEE:mailto:user11 at example.org
+ATTENDEE:mailto:user12 at example.org
+ATTENDEE:mailto:user13 at example.org
+ATTENDEE:mailto:user14 at example.org
+ATTENDEE:mailto:user15 at example.org
+ATTENDEE:mailto:user16 at example.org
+ATTENDEE:mailto:user17 at example.org
+ATTENDEE:mailto:user18 at example.org
+ATTENDEE:mailto:user19 at example.org
+ATTENDEE:mailto:user20 at example.org
+ATTENDEE:mailto:user21 at example.org
+ATTENDEE:mailto:user22 at example.org
+ATTENDEE:mailto:user23 at example.org
+ATTENDEE:mailto:user24 at example.org
+ATTENDEE:mailto:user25 at example.org
+DTSTAMP:20051222T210507Z
+ORGANIZER;SCHEDULE-AGENT=NONE;SCHEDULE-STATUS=5.3:mailto:user1 at example.org
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.org
+ATTENDEE:mailto:user2 at example.org
+DTSTAMP:20051222T210507Z
+ORGANIZER;SCHEDULE-AGENT=NONE;SCHEDULE-STATUS=5.3:mailto:user1 at example.org
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE:mailto:user1 at example.org
+ATTENDEE:mailto:user2 at example.org
+DTSTAMP:20051222T210507Z
+ORGANIZER;SCHEDULE-AGENT=NONE;SCHEDULE-STATUS=5.3:mailto:user1 at example.org
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+"""
+
+ component = Component.fromString(data % self.subs)
+ cobj = yield calendar.createCalendarObjectWithName("data1.ics", component)
+ self.assertFalse(hasattr(cobj, "_workItems"))
+ yield self.commit()
+
+ w = schema.CALENDAR_OBJECT_SPLITTER_WORK
+ rows = yield Select(
+ [w.RESOURCE_ID, ],
+ From=w
+ ).on(self.transactionUnderTest())
+ self.assertEqual(len(rows), 0)
+ yield self.abort()
+
+ # Do manual split
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ will = yield cobj.willSplit()
+ self.assertTrue(will)
+
+ newUID = yield cobj.split()
+ yield self.commit()
+
+ cobj1 = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ cobj2 = yield self.calendarObjectUnderTest(name="%s.ics" % (newUID,), calendar_name="calendar", home="user01")
+ self.assertTrue(cobj2 is not None)
+
+ ical_future = yield cobj1.component()
+ ical_past = yield cobj2.component()
+
+ title = "temp"
+ relsubs = dict(self.subs)
+ relsubs["relID"] = newUID
+ self.assertEqual(str(ical_future).replace("\r\n ", ""), data_future.replace("\n", "\r\n") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(ical_past).replace("\r\n ", ""), data_past.replace("\n", "\r\n") % relsubs, "Failed past: %s" % (title,))
+
+
+ @inlineCallbacks
+ def test_calendarObjectSplit_work(self):
+ """
+ Test that splitting of calendar objects works.
+ """
+ self.patch(config.Scheduling.Options.Splitting, "Enabled", True)
+ self.patch(config.Scheduling.Options.Splitting, "Size", 1024)
+ self.patch(config.Scheduling.Options.Splitting, "PastDays", 14)
+ self.patch(config.Scheduling.Options.Splitting, "Delay", 2)
+
+ # Create one event that will split
+ calendar = yield self.calendarUnderTest(name="calendar", home="user01")
+
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user04 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user05 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_future = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 05;EMAIL=user05 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user05
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_past = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+ATTENDEE;CN=User 04;EMAIL=user04 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user04
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_future2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+EXDATE:%(now_fwd10)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_past2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 04;EMAIL=user04 at example.com;RSVP=TRUE:urn:uuid:user04
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:%(relID)s
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_inbox2 = """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+EXDATE:%(now_fwd10)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_future3 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+EXDATE:%(now_fwd10)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user03
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_past3 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+EXDATE:%(now_back25)s
+EXDATE:%(now_back24)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:%(relID)s
+X-CALENDARSERVER-PERUSER-UID:user03
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_inbox3 = """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 03;EMAIL=user03 at example.com;RSVP=TRUE:urn:uuid:user03
+DTSTAMP:20051222T210507Z
+EXDATE:%(now_fwd10)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_past4 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+ATTENDEE;CN=User 04;EMAIL=user04 at example.com;RSVP=TRUE:urn:uuid:user04
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:%(relID)s
+X-CALENDARSERVER-PERUSER-UID:user04
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:%(now_back25)s
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_future5 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 05;EMAIL=user05 at example.com;RSVP=TRUE:urn:uuid:user05
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user05
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:%(now_fwd10)s
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_inbox5 = """BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 05;EMAIL=user05 at example.com;RSVP=TRUE:urn:uuid:user05
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+END:VEVENT
+END:VCALENDAR
+"""
+
+ component = Component.fromString(data % self.subs)
+ cobj = yield calendar.createCalendarObjectWithName("data1.ics", component)
+ self.assertTrue(hasattr(cobj, "_workItems"))
+ work = cobj._workItems[0]
+ yield self.commit()
+
+ w = schema.CALENDAR_OBJECT_SPLITTER_WORK
+ rows = yield Select(
+ [w.RESOURCE_ID, ],
+ From=w
+ ).on(self.transactionUnderTest())
+ self.assertEqual(len(rows), 1)
+ self.assertEqual(rows[0][0], cobj._resourceID)
+ yield self.abort()
+
+ # Wait for it to complete
+ yield work.whenExecuted()
+
+ rows = yield Select(
+ [w.RESOURCE_ID, ],
+ From=w
+ ).on(self.transactionUnderTest())
+ self.assertEqual(len(rows), 0)
+ yield self.abort()
+
+ # Get the existing and new object data
+ cobj1 = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ self.assertTrue(cobj1.isScheduleObject)
+ ical1 = yield cobj1.component()
+ newUID = ical1.masterComponent().propertyValue("RELATED-TO")
+
+ cobj2 = yield self.calendarObjectUnderTest(name="%s.ics" % (newUID,), calendar_name="calendar", home="user01")
+ self.assertTrue(cobj2 is not None)
+ self.assertTrue(cobj2.isScheduleObject)
+
+ ical_future = yield cobj1.component()
+ ical_past = yield cobj2.component()
+
+ # Verify user01 data
+ title = "user01"
+ relsubs = dict(self.subs)
+ relsubs["relID"] = newUID
+ self.assertEqual(str(ical_future).replace("\r\n ", ""), data_future.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(ical_past).replace("\r\n ", ""), data_past.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed past: %s" % (title,))
+
+ # Get user02 data
+ cal = yield self.calendarUnderTest(name="calendar", home="user02")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 2)
+ for cobj in cobjs:
+ ical = yield cobj.component()
+ if ical.resourceUID() == "12345-67890":
+ ical_future = ical
+ else:
+ ical_past = ical
+
+ cal = yield self.calendarUnderTest(name="inbox", home="user02")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 1)
+ ical_inbox = yield cobjs[0].component()
+
+ # Verify user02 data
+ title = "user02"
+ self.assertEqual(str(ical_future).replace("\r\n ", ""), data_future2.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(ical_past).replace("\r\n ", ""), data_past2.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed past: %s" % (title,))
+ self.assertEqual(str(ical_inbox).replace("\r\n ", ""), data_inbox2.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed inbox: %s" % (title,))
+
+ # Get user03 data
+ cal = yield self.calendarUnderTest(name="calendar", home="user03")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 2)
+ for cobj in cobjs:
+ ical = yield cobj.component()
+ if ical.resourceUID() == "12345-67890":
+ ical_future = ical
+ else:
+ ical_past = ical
+ self.assertTrue(cobj.isScheduleObject)
+
+ cal = yield self.calendarUnderTest(name="inbox", home="user03")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 1)
+ ical_inbox = yield cobjs[0].component()
+
+ # Verify user03 data
+ title = "user03"
+ self.assertEqual(str(ical_future).replace("\r\n ", ""), data_future3.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(ical_past).replace("\r\n ", ""), data_past3.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed past: %s" % (title,))
+ self.assertEqual(str(ical_inbox).replace("\r\n ", ""), data_inbox3.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed inbox: %s" % (title,))
+
+ # Get user04 data
+ cal = yield self.calendarUnderTest(name="calendar", home="user04")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 1)
+ ical_past = yield cobjs[0].component()
+ self.assertTrue(cobjs[0].isScheduleObject)
+
+ cal = yield self.calendarUnderTest(name="inbox", home="user04")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 0)
+
+ # Verify user04 data
+ title = "user04"
+ self.assertEqual(str(ical_past).replace("\r\n ", ""), data_past4.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed past: %s" % (title,))
+
+ # Get user05 data
+ cal = yield self.calendarUnderTest(name="calendar", home="user05")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 1)
+ ical_future = yield cobjs[0].component()
+ self.assertTrue(cobjs[0].isScheduleObject)
+
+ cal = yield self.calendarUnderTest(name="inbox", home="user05")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 1)
+ ical_inbox = yield cobjs[0].component()
+
+ # Verify user05 data
+ title = "user05"
+ self.assertEqual(str(ical_future).replace("\r\n ", ""), data_future5.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(ical_inbox).replace("\r\n ", ""), data_inbox5.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed inbox: %s" % (title,))
+
+
+ @inlineCallbacks
+ def test_calendarObjectSplit_removed(self):
+ """
+ Test that splitting of calendar objects dioes not occur when the object is
+ removed before the work can be done.
+ """
+ self.patch(config.Scheduling.Options.Splitting, "Enabled", True)
+ self.patch(config.Scheduling.Options.Splitting, "Size", 1024)
+ self.patch(config.Scheduling.Options.Splitting, "PastDays", 14)
+ self.patch(config.Scheduling.Options.Splitting, "Delay", 10)
+
+ # Create one event that will split
+ calendar = yield self.calendarUnderTest(name="calendar", home="user01")
+
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user04 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user05 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ component = Component.fromString(data % self.subs)
+ cobj = yield calendar.createCalendarObjectWithName("data1.ics", component)
+ self.assertTrue(hasattr(cobj, "_workItems"))
+ work = cobj._workItems[0]
+ yield self.commit()
+
+ w = schema.CALENDAR_OBJECT_SPLITTER_WORK
+ rows = yield Select(
+ [w.RESOURCE_ID, ],
+ From=w
+ ).on(self.transactionUnderTest())
+ self.assertEqual(len(rows), 1)
+ self.assertEqual(rows[0][0], cobj._resourceID)
+ yield self.abort()
+
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ yield cobj.remove()
+ yield self.commit()
+
+ rows = yield Select(
+ [w.RESOURCE_ID, ],
+ From=w
+ ).on(self.transactionUnderTest())
+ self.assertEqual(len(rows), 0)
+ yield self.abort()
+
+ # Wait for it to complete
+ yield work.whenExecuted()
+
+ rows = yield Select(
+ [w.RESOURCE_ID, ],
+ From=w
+ ).on(self.transactionUnderTest())
+ self.assertEqual(len(rows), 0)
+ yield self.abort()
+
+ cal = yield self.calendarUnderTest(name="calendar", home="user01")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 0)
+
+
+ @inlineCallbacks
+ def test_calendarObjectSplit_no_attendee_split(self):
+ """
+ Test that calendar objects do not split on attendee change.
+ """
+ self.patch(config.Scheduling.Options.Splitting, "Enabled", True)
+ self.patch(config.Scheduling.Options.Splitting, "Size", 1024)
+ self.patch(config.Scheduling.Options.Splitting, "PastDays", 14)
+ self.patch(config.Scheduling.Options.Splitting, "Delay", 2)
+
+ # Create one event that will not split
+ calendar = yield self.calendarUnderTest(name="calendar", home="user01")
+
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_2_update = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+TRANSP:TRANSPARENT
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+TRANSP:TRANSPARENT
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+TRANSP:TRANSPARENT
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_2_changed = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:20051222T210507Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:%(now_back25)s
+TRANSP:TRANSPARENT
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+RECURRENCE-ID:%(now_fwd10)s
+TRANSP:TRANSPARENT
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ component = Component.fromString(data % self.subs)
+ cobj = yield calendar.createCalendarObjectWithName("data1.ics", component)
+ self.assertFalse(hasattr(cobj, "_workItems"))
+ yield self.commit()
+
+ # Get user02 data
+ cal = yield self.calendarUnderTest(name="calendar", home="user02")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 1)
+ cobj = cobjs[0]
+ cname2 = cobj.name()
+ ical = yield cobj.component()
+ self.assertEqual(str(ical).replace("\r\n ", ""), data_2.replace("\n", "\r\n").replace("\r\n ", "") % self.subs, "Failed 2")
+ yield cobj.setComponent(Component.fromString(data_2_update % self.subs))
+ yield self.commit()
+
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ ical = yield cobj.component()
+ self.assertEqual(str(ical).replace("\r\n ", ""), data_1.replace("\n", "\r\n").replace("\r\n ", "") % self.subs, "Failed 2")
+ cobj = yield self.calendarObjectUnderTest(name=cname2, calendar_name="calendar", home="user02")
+ ical = yield cobj.component()
+ self.assertEqual(str(ical).replace("\r\n ", ""), data_2_changed.replace("\n", "\r\n").replace("\r\n ", "") % self.subs, "Failed 2")
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_calendarObjectSplit_no_non_organizer_split(self):
+ """
+ Test that calendar objects do not split on attendee change.
+ """
+ self.patch(config.Scheduling.Options.Splitting, "Enabled", True)
+ self.patch(config.Scheduling.Options.Splitting, "Size", 1024)
+ self.patch(config.Scheduling.Options.Splitting, "PastDays", 14)
+ self.patch(config.Scheduling.Options.Splitting, "Delay", 2)
+
+ # Create one event that will not split
+ calendar = yield self.calendarUnderTest(name="calendar", home="user01")
+
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+RRULE:FREQ=DAILY
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+END:VCALENDAR
+"""
+
+ component = Component.fromString(data % self.subs)
+ cobj = yield calendar.createCalendarObjectWithName("data1.ics", component)
+ self.assertFalse(hasattr(cobj, "_workItems"))
+ yield self.commit()
+
+
+ @inlineCallbacks
+ def test_calendarObjectSplit_attachments(self):
+ """
+ Test that splitting of calendar objects with managed attachments works.
+ """
+ self.patch(config.Scheduling.Options.Splitting, "Enabled", True)
+ self.patch(config.Scheduling.Options.Splitting, "Size", 1024)
+ self.patch(config.Scheduling.Options.Splitting, "PastDays", 14)
+ self.patch(config.Scheduling.Options.Splitting, "Delay", 2)
+
+ # Create one event that will split
+ calendar = yield self.calendarUnderTest(name="calendar", home="user01")
+
+ data_1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+DTSTAMP:20051222T210507Z
+ORGANIZER:mailto:user01 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_attach_1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RRULE:FREQ=DAILY
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_split_1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RRULE:FREQ=DAILY
+SEQUENCE:1
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+SEQUENCE:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+SEQUENCE:1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_future = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+SEQUENCE:2
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+SEQUENCE:2
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_past = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(past_mid)s;SIZE=14:%(att_past_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s
+SEQUENCE:2
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(past_mid)s;SIZE=14:%(att_past_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+SEQUENCE:2
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(past_mid)s;SIZE=14:%(att_past_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+SEQUENCE:2
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_future2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:%(now_back14)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY
+SEQUENCE:2
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:%(now_fwd10)s
+DTSTART:%(now_fwd10)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(mid)s;SIZE=14:%(att_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+SEQUENCE:2
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ data_past2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:%(relID)s
+DTSTART:%(now_back30)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(past_mid)s;SIZE=14:%(att_past_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+RRULE:FREQ=DAILY;UNTIL=%(now_back14_1)s
+SEQUENCE:2
+SUMMARY:1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back25)s
+DTSTART:%(now_back25)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(past_mid)s;SIZE=14:%(att_past_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+SEQUENCE:2
+END:VEVENT
+BEGIN:VEVENT
+UID:%(relID)s
+RECURRENCE-ID:%(now_back24)s
+DTSTART:%(now_back24)s
+DURATION:PT1H
+ATTACH;FILENAME=new.attachment;FMTTYPE=text/x-fixture;MANAGED-ID=%(past_mid)s;SIZE=14:%(att_past_uri)s
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;PARTSTAT=ACCEPTED:urn:uuid:user01
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:user02
+DTSTAMP:%(dtstamp)s
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:user01
+RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:%(relID)s
+SEQUENCE:2
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:%(relID)s
+X-CALENDARSERVER-PERUSER-UID:user02
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+TRANSP:TRANSPARENT
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+"""
+
+ # Create initial non-split event
+ cobj = yield calendar.createCalendarObjectWithName("data1.ics", Component.fromString(data_1 % self.subs))
+ self.assertFalse(hasattr(cobj, "_workItems"))
+ yield self.commit()
+
+ # Add a managed attachment
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ attachment, location = yield cobj.addAttachment(None, MimeType("text", "x-fixture"), "new.attachment", MemoryStream("new attachment"))
+ mid = attachment.managedID()
+ yield self.commit()
+
+ # Get attachment details
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ ical = yield cobj.component()
+ attachment = ical.masterComponent().getProperty("ATTACH")
+ self.assertEqual(attachment.parameterValue("MANAGED-ID"), mid)
+ self.assertEqual(attachment.value(), location)
+
+ relsubs = dict(self.subs)
+ relsubs["mid"] = mid
+ relsubs["att_uri"] = location
+ relsubs["dtstamp"] = str(ical.masterComponent().propertyValue("DTSTAMP"))
+ self.assertEqual(str(ical).replace("\r\n ", ""), data_attach_1.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed attachment user01")
+ yield self.commit()
+
+ # Add overrides to cause a split
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ yield cobj.setComponent(Component.fromString(data_split_1 % relsubs))
+ self.assertTrue(hasattr(cobj, "_workItems"))
+ work = cobj._workItems[0]
+ yield self.commit()
+
+ # Wait for it to complete
+ yield work.whenExecuted()
+
+ # Get the existing and new object data
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
+ ical_future = yield cobj.component()
+ newUID = ical_future.masterComponent().propertyValue("RELATED-TO")
+ relsubs["relID"] = newUID
+ relsubs["dtstamp"] = str(ical_future.masterComponent().propertyValue("DTSTAMP"))
+
+ cobj = yield self.calendarObjectUnderTest(name="%s.ics" % (newUID,), calendar_name="calendar", home="user01")
+ self.assertTrue(cobj is not None)
+ ical_past = yield cobj.component()
+ attachment = ical.masterComponent().getProperty("ATTACH")
+ self.assertEqual(attachment.parameterValue("MANAGED-ID"), mid)
+ self.assertEqual(attachment.value(), location)
+
+ relsubs["past_mid"] = attachment.parameterValue("MANAGED-ID")
+ relsubs["att_past_uri"] = attachment.value()
+
+ # Verify user01 data
+ title = "user01"
+ self.assertEqual(str(ical_future).replace("\r\n ", ""), data_future.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(ical_past).replace("\r\n ", ""), data_past.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed past: %s" % (title,))
+
+ # Get user02 data
+ cal = yield self.calendarUnderTest(name="calendar", home="user02")
+ cobjs = yield cal.calendarObjects()
+ self.assertEqual(len(cobjs), 2)
+ for cobj in cobjs:
+ ical = yield cobj.component()
+ if ical.resourceUID() == "12345-67890":
+ ical_future = ical
+ else:
+ ical_past = ical
+
+ # Verify user02 data
+ title = "user02"
+ self.assertEqual(str(ical_future).replace("\r\n ", ""), data_future2.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed future: %s" % (title,))
+ self.assertEqual(str(ical_past).replace("\r\n ", ""), data_past2.replace("\n", "\r\n").replace("\r\n ", "") % relsubs, "Failed past: %s" % (title,))
Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -863,6 +863,9 @@
ATTACHMENT_UPDATE - change to a managed attachment that is re-writing calendar data.
+ SPLIT - calendar data is being split. Some validation and implicit scheduling is not done.
+ Schedule-Tag is changed.
+
RAW - store the supplied data as-is without any processing or validation. This is used
for unit testing purposes only.
"""
@@ -872,6 +875,7 @@
ORGANIZER_ITIP_UPDATE = NamedConstant()
ATTENDEE_ITIP_UPDATE = NamedConstant()
ATTACHMENT_UPDATE = NamedConstant()
+ SPLIT = NamedConstant()
RAW = NamedConstant()
NORMAL.description = "normal"
@@ -879,6 +883,7 @@
ORGANIZER_ITIP_UPDATE.description = "organizer-update"
ATTENDEE_ITIP_UPDATE.description = "attendee-update"
ATTACHMENT_UPDATE.description = "attachment-update"
+ SPLIT.description = "split"
RAW.description = "raw"
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2013-07-04 02:29:51 UTC (rev 11460)
@@ -203,7 +203,7 @@
@inlineCallbacks
- def _withEachHomeDo(self, homeTable, homeFromTxn, action, batchSize): #@UnusedVariable
+ def _withEachHomeDo(self, homeTable, homeFromTxn, action, batchSize): #@UnusedVariable
"""
Implementation of L{ICalendarStore.withEachCalendarHomeDo} and
L{IAddressbookStore.withEachAddressbookHomeDo}.
@@ -503,7 +503,7 @@
@classproperty
- def _calendarserver(cls): #@NoSelf
+ def _calendarserver(cls): #@NoSelf
cs = schema.CALENDARSERVER
return Select(
[cs.VALUE, ],
@@ -548,7 +548,7 @@
return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
- def _determineMemo(self, storeType, uid, create=False): #@UnusedVariable
+ def _determineMemo(self, storeType, uid, create=False): #@UnusedVariable
"""
Determine the memo dictionary to use for homeWithUID.
"""
@@ -612,7 +612,7 @@
@classproperty
- def _insertAPNSubscriptionQuery(cls): #@NoSelf
+ def _insertAPNSubscriptionQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Insert({apn.TOKEN: Parameter("token"),
apn.RESOURCE_KEY: Parameter("resourceKey"),
@@ -623,7 +623,7 @@
@classproperty
- def _updateAPNSubscriptionQuery(cls): #@NoSelf
+ def _updateAPNSubscriptionQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Update({apn.MODIFIED: Parameter("modified"),
apn.SUBSCRIBER_GUID: Parameter("subscriber"),
@@ -634,7 +634,7 @@
@classproperty
- def _selectAPNSubscriptionQuery(cls): #@NoSelf
+ def _selectAPNSubscriptionQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Select([apn.MODIFIED, apn.SUBSCRIBER_GUID], From=apn,
Where=(
@@ -678,7 +678,7 @@
@classproperty
- def _removeAPNSubscriptionQuery(cls): #@NoSelf
+ def _removeAPNSubscriptionQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Delete(From=apn,
Where=(apn.TOKEN == Parameter("token")).And(
@@ -691,7 +691,7 @@
@classproperty
- def _purgeOldAPNSubscriptionQuery(cls): #@NoSelf
+ def _purgeOldAPNSubscriptionQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Delete(From=apn,
Where=(apn.MODIFIED < Parameter("olderThan")))
@@ -703,7 +703,7 @@
@classproperty
- def _apnSubscriptionsByTokenQuery(cls): #@NoSelf
+ def _apnSubscriptionsByTokenQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Select([apn.RESOURCE_KEY, apn.MODIFIED, apn.SUBSCRIBER_GUID],
From=apn, Where=apn.TOKEN == Parameter("token"))
@@ -714,7 +714,7 @@
@classproperty
- def _apnSubscriptionsByKeyQuery(cls): #@NoSelf
+ def _apnSubscriptionsByKeyQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Select([apn.TOKEN, apn.SUBSCRIBER_GUID],
From=apn, Where=apn.RESOURCE_KEY == Parameter("resourceKey"))
@@ -725,7 +725,7 @@
@classproperty
- def _apnSubscriptionsBySubscriberQuery(cls): #@NoSelf
+ def _apnSubscriptionsBySubscriberQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Select([apn.TOKEN, apn.RESOURCE_KEY, apn.MODIFIED, apn.USER_AGENT, apn.IP_ADDR],
From=apn, Where=apn.SUBSCRIBER_GUID == Parameter("subscriberGUID"))
@@ -738,7 +738,7 @@
# Create IMIP token
@classproperty
- def _insertIMIPTokenQuery(cls): #@NoSelf
+ def _insertIMIPTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Insert({imip.TOKEN: Parameter("token"),
imip.ORGANIZER: Parameter("organizer"),
@@ -768,7 +768,7 @@
@classproperty
- def _selectIMIPTokenByTokenQuery(cls): #@NoSelf
+ def _selectIMIPTokenByTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Select([imip.ORGANIZER, imip.ATTENDEE, imip.ICALUID], From=imip,
Where=(imip.TOKEN == Parameter("token")))
@@ -781,7 +781,7 @@
@classproperty
- def _selectIMIPTokenQuery(cls): #@NoSelf
+ def _selectIMIPTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Select([imip.TOKEN], From=imip,
Where=(imip.ORGANIZER == Parameter("organizer")).And(
@@ -790,7 +790,7 @@
@classproperty
- def _updateIMIPTokenQuery(cls): #@NoSelf
+ def _updateIMIPTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Update({imip.ACCESSED: utcNowSQL, },
Where=(imip.ORGANIZER == Parameter("organizer")).And(
@@ -814,7 +814,7 @@
# Remove IMIP token
@classproperty
- def _removeIMIPTokenQuery(cls): #@NoSelf
+ def _removeIMIPTokenQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Delete(From=imip,
Where=(imip.TOKEN == Parameter("token")))
@@ -826,7 +826,7 @@
# Purge old IMIP tokens
@classproperty
- def _purgeOldIMIPTokensQuery(cls): #@NoSelf
+ def _purgeOldIMIPTokensQuery(cls): #@NoSelf
imip = schema.IMIP_TOKENS
return Delete(From=imip,
Where=(imip.ACCESSED < Parameter("olderThan")))
@@ -1356,7 +1356,7 @@
count = 0
for _ignore, managedID in results:
- attachment = (yield ManagedAttachment.load(self, managedID))
+ attachment = (yield ManagedAttachment.load(self, None, managedID))
yield attachment.remove()
count += 1
returnValue(count)
@@ -1365,21 +1365,23 @@
def acquireUpgradeLock(self):
return self.execSQL("select pg_advisory_lock(1)")
+
def releaseUpgradeLock(self):
return self.execSQL("select pg_advisory_unlock(1)")
+
class _EmptyCacher(object):
- def set(self, key, value): #@UnusedVariable
+ def set(self, key, value): #@UnusedVariable
return succeed(True)
- def get(self, key, withIdentifier=False): #@UnusedVariable
+ def get(self, key, withIdentifier=False): #@UnusedVariable
return succeed(None)
- def delete(self, key): #@UnusedVariable
+ def delete(self, key): #@UnusedVariable
return succeed(True)
@@ -1429,14 +1431,14 @@
@classproperty
- def _resourceIDFromOwnerQuery(cls): #@NoSelf
+ def _resourceIDFromOwnerQuery(cls): #@NoSelf
home = cls._homeSchema
return Select([home.RESOURCE_ID],
From=home, Where=home.OWNER_UID == Parameter("ownerUID"))
@classproperty
- def _ownerFromResourceID(cls): #@NoSelf
+ def _ownerFromResourceID(cls): #@NoSelf
home = cls._homeSchema
return Select([home.OWNER_UID],
From=home,
@@ -1444,7 +1446,7 @@
@classproperty
- def _metaDataQuery(cls): #@NoSelf
+ def _metaDataQuery(cls): #@NoSelf
metadata = cls._homeMetaDataSchema
return Select(cls.metadataColumns(),
From=metadata,
@@ -1762,7 +1764,7 @@
@classproperty
- def _syncTokenQuery(cls): #@NoSelf
+ def _syncTokenQuery(cls): #@NoSelf
"""
DAL Select statement to find the sync token.
@@ -1826,7 +1828,7 @@
@classproperty
- def _changesQuery(cls): #@NoSelf
+ def _changesQuery(cls): #@NoSelf
bind = cls._bindSchema
rev = cls._revisionsSchema
return Select(
@@ -2032,12 +2034,12 @@
@classproperty
- def _resourceByUIDQuery(cls): #@NoSelf
+ def _resourceByUIDQuery(cls): #@NoSelf
return cls._objectResourceQuery(checkBindMode=False)
@classproperty
- def _resourceByUIDBindQuery(cls): #@NoSelf
+ def _resourceByUIDBindQuery(cls): #@NoSelf
return cls._objectResourceQuery(checkBindMode=True)
@@ -2098,7 +2100,7 @@
@classproperty
- def _quotaQuery(cls): #@NoSelf
+ def _quotaQuery(cls): #@NoSelf
meta = cls._homeMetaDataSchema
return Select(
[meta.QUOTA_USED_BYTES], From=meta,
@@ -2115,7 +2117,7 @@
@classproperty
- def _preLockResourceIDQuery(cls): #@NoSelf
+ def _preLockResourceIDQuery(cls): #@NoSelf
meta = cls._homeMetaDataSchema
return Select(From=meta,
Where=meta.RESOURCE_ID == Parameter("resourceID"),
@@ -2123,7 +2125,7 @@
@classproperty
- def _increaseQuotaQuery(cls): #@NoSelf
+ def _increaseQuotaQuery(cls): #@NoSelf
meta = cls._homeMetaDataSchema
return Update({meta.QUOTA_USED_BYTES: meta.QUOTA_USED_BYTES +
Parameter("delta")},
@@ -2132,7 +2134,7 @@
@classproperty
- def _resetQuotaQuery(cls): #@NoSelf
+ def _resetQuotaQuery(cls): #@NoSelf
meta = cls._homeMetaDataSchema
return Update({meta.QUOTA_USED_BYTES: 0},
Where=meta.RESOURCE_ID == Parameter("resourceID"))
@@ -2198,7 +2200,7 @@
@classproperty
- def _lockLastModifiedQuery(cls): #@NoSelf
+ def _lockLastModifiedQuery(cls): #@NoSelf
meta = cls._homeMetaDataSchema
return Select(
From=meta,
@@ -2209,7 +2211,7 @@
@classproperty
- def _changeLastModifiedQuery(cls): #@NoSelf
+ def _changeLastModifiedQuery(cls): #@NoSelf
meta = cls._homeMetaDataSchema
return Update({meta.MODIFIED: utcNowSQL},
Where=meta.RESOURCE_ID == Parameter("resourceID"),
@@ -2276,7 +2278,7 @@
"""
@classproperty
- def _childSyncTokenQuery(cls): #@NoSelf
+ def _childSyncTokenQuery(cls): #@NoSelf
"""
DAL query for retrieving the sync token of a L{CommonHomeChild} based on
its resource ID.
@@ -2309,7 +2311,7 @@
@classproperty
- def _objectNamesSinceRevisionQuery(cls): #@NoSelf
+ def _objectNamesSinceRevisionQuery(cls): #@NoSelf
"""
DAL query for (resource, deleted-flag)
"""
@@ -2363,7 +2365,7 @@
@classproperty
- def _removeDeletedRevision(cls): #@NoSelf
+ def _removeDeletedRevision(cls): #@NoSelf
rev = cls._revisionsSchema
return Delete(From=rev,
Where=(rev.HOME_RESOURCE_ID == Parameter("homeID")).And(
@@ -2371,7 +2373,7 @@
@classproperty
- def _addNewRevision(cls): #@NoSelf
+ def _addNewRevision(cls): #@NoSelf
rev = cls._revisionsSchema
return Insert({rev.HOME_RESOURCE_ID: Parameter("homeID"),
rev.RESOURCE_ID: Parameter("resourceID"),
@@ -2396,7 +2398,7 @@
@classproperty
- def _renameSyncTokenQuery(cls): #@NoSelf
+ def _renameSyncTokenQuery(cls): #@NoSelf
"""
DAL query to change sync token for a rename (increment and adjust
resource name).
@@ -2421,7 +2423,7 @@
@classproperty
- def _bumpSyncTokenQuery(cls): #@NoSelf
+ def _bumpSyncTokenQuery(cls): #@NoSelf
"""
DAL query to change collection sync token.
"""
@@ -2444,7 +2446,7 @@
@classproperty
- def _deleteSyncTokenQuery(cls): #@NoSelf
+ def _deleteSyncTokenQuery(cls): #@NoSelf
"""
DAL query to update a sync revision to be a tombstone instead.
"""
@@ -2458,7 +2460,7 @@
@classproperty
- def _sharedRemovalQuery(cls): #@NoSelf
+ def _sharedRemovalQuery(cls): #@NoSelf
"""
DAL query to update the sync token for a shared collection.
"""
@@ -2473,7 +2475,7 @@
@classproperty
- def _unsharedRemovalQuery(cls): #@NoSelf
+ def _unsharedRemovalQuery(cls): #@NoSelf
"""
DAL query to update the sync token for an owned collection.
"""
@@ -2520,7 +2522,7 @@
@classproperty
- def _deleteBumpTokenQuery(cls): #@NoSelf
+ def _deleteBumpTokenQuery(cls): #@NoSelf
rev = cls._revisionsSchema
return Update({rev.REVISION: schema.REVISION_SEQ,
rev.DELETED: True},
@@ -2530,7 +2532,7 @@
@classproperty
- def _updateBumpTokenQuery(cls): #@NoSelf
+ def _updateBumpTokenQuery(cls): #@NoSelf
rev = cls._revisionsSchema
return Update({rev.REVISION: schema.REVISION_SEQ},
Where=(rev.RESOURCE_ID == Parameter("resourceID")).And(
@@ -2539,7 +2541,7 @@
@classproperty
- def _insertFindPreviouslyNamedQuery(cls): #@NoSelf
+ def _insertFindPreviouslyNamedQuery(cls): #@NoSelf
rev = cls._revisionsSchema
return Select([rev.RESOURCE_ID], From=rev,
Where=(rev.RESOURCE_ID == Parameter("resourceID")).And(
@@ -2547,7 +2549,7 @@
@classproperty
- def _updatePreviouslyNamedQuery(cls): #@NoSelf
+ def _updatePreviouslyNamedQuery(cls): #@NoSelf
rev = cls._revisionsSchema
return Update({rev.REVISION: schema.REVISION_SEQ,
rev.DELETED: False},
@@ -2557,7 +2559,7 @@
@classproperty
- def _completelyNewRevisionQuery(cls): #@NoSelf
+ def _completelyNewRevisionQuery(cls): #@NoSelf
rev = cls._revisionsSchema
return Insert({rev.HOME_RESOURCE_ID: Parameter("homeID"),
rev.RESOURCE_ID: Parameter("resourceID"),
@@ -2624,7 +2626,7 @@
"""
@classproperty
- def _bindInsertQuery(cls, **kw): #@NoSelf #@UnusedVariable
+ def _bindInsertQuery(cls, **kw): #@NoSelf #@UnusedVariable
"""
DAL statement to create a bind entry that connects a collection to its
home.
@@ -2641,7 +2643,7 @@
@classmethod
- def _updateBindColumnsQuery(cls, columnMap): #@NoSelf
+ def _updateBindColumnsQuery(cls, columnMap): #@NoSelf
bind = cls._bindSchema
return Update(columnMap,
Where=(bind.RESOURCE_ID == Parameter("resourceID"))
@@ -2650,7 +2652,7 @@
@classproperty
- def _updateBindQuery(cls): #@NoSelf
+ def _updateBindQuery(cls): #@NoSelf
bind = cls._bindSchema
return cls._updateBindColumnsQuery(
{bind.BIND_MODE: Parameter("mode"),
@@ -2659,7 +2661,7 @@
@classproperty
- def _deleteBindForResourceIDAndHomeID(cls): #@NoSelf
+ def _deleteBindForResourceIDAndHomeID(cls): #@NoSelf
bind = cls._bindSchema
return Delete(
From=bind,
@@ -2670,7 +2672,7 @@
@classmethod
- def _bindFor(cls, condition): #@NoSelf
+ def _bindFor(cls, condition): #@NoSelf
bind = cls._bindSchema
columns = cls.bindColumns() + cls.additionalBindColumns()
return Select(
@@ -2681,7 +2683,7 @@
@classproperty
- def _sharedBindForResourceID(cls): #@NoSelf
+ def _sharedBindForResourceID(cls): #@NoSelf
bind = cls._bindSchema
return cls._bindFor((bind.RESOURCE_ID == Parameter("resourceID"))
.And(bind.BIND_STATUS == _BIND_STATUS_ACCEPTED)
@@ -2690,14 +2692,14 @@
@classproperty
- def _acceptedBindForHomeID(cls): #@NoSelf
+ def _acceptedBindForHomeID(cls): #@NoSelf
bind = cls._bindSchema
return cls._bindFor((bind.HOME_RESOURCE_ID == Parameter("homeID"))
.And(bind.BIND_STATUS == _BIND_STATUS_ACCEPTED))
@classproperty
- def _unacceptedBindForResourceID(cls): #@NoSelf
+ def _unacceptedBindForResourceID(cls): #@NoSelf
bind = cls._bindSchema
return cls._bindFor((bind.RESOURCE_ID == Parameter("resourceID"))
.And(bind.BIND_STATUS != _BIND_STATUS_ACCEPTED)
@@ -2705,7 +2707,7 @@
@classproperty
- def _bindForResourceIDAndHomeID(cls): #@NoSelf
+ def _bindForResourceIDAndHomeID(cls): #@NoSelf
"""
DAL query that looks up home bind rows by home child
resource ID and home resource ID.
@@ -2717,7 +2719,7 @@
@classproperty
- def _bindForNameAndHomeID(cls): #@NoSelf
+ def _bindForNameAndHomeID(cls): #@NoSelf
"""
DAL query that looks up any bind rows by home child
resource ID and home resource ID.
@@ -2946,7 +2948,7 @@
result = []
for row in acceptedRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount] #@UnusedVariable
home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
new = yield home.objectWithShareUID(bindName)
result.append(new)
@@ -2975,7 +2977,7 @@
result = []
for row in rows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:self.bindColumnCount] #@UnusedVariable
home = yield self._txn.homeWithResourceID(self._home._homeType, homeID)
new = yield home.invitedObjectWithShareUID(bindName)
result.append(new)
@@ -3144,7 +3146,7 @@
@classproperty
- def _childrenAndMetadataForHomeID(cls): #@NoSelf
+ def _childrenAndMetadataForHomeID(cls): #@NoSelf
bind = cls._bindSchema
child = cls._homeChildSchema
childMetaData = cls._homeChildMetaDataSchema
@@ -3234,7 +3236,7 @@
self._index = None # Derived classes need to set this
- def memoMe(self, key, memo): #@UnusedVariable
+ def memoMe(self, key, memo): #@UnusedVariable
"""
Add this object to the memo dictionary in whatever fashion is appropriate.
@@ -3292,7 +3294,7 @@
# Create the actual objects merging in properties
for dataRow in dataRows:
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = dataRow[:cls.bindColumnCount] #@UnusedVariable
additionalBind = dataRow[cls.bindColumnCount:cls.bindColumnCount + len(cls.additionalBindColumns())]
metadata = dataRow[cls.bindColumnCount + len(cls.additionalBindColumns()):]
@@ -3361,7 +3363,7 @@
returnValue(None)
row = rows[0]
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable
if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
returnValue(None)
additionalBind = row[cls.bindColumnCount:cls.bindColumnCount + len(cls.additionalBindColumns())]
@@ -3402,7 +3404,7 @@
returnValue(None)
row = rows[0]
- bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable]
+ bindMode, homeID, resourceID, bindName, bindStatus, bindRevision, bindMessage = row[:cls.bindColumnCount] #@UnusedVariable]
if (bindStatus == _BIND_STATUS_ACCEPTED) != bool(accepted):
returnValue(None)
@@ -3413,7 +3415,7 @@
@classproperty
- def _insertHomeChild(cls): #@NoSelf
+ def _insertHomeChild(cls): #@NoSelf
"""
DAL statement to create a home child with all default values.
"""
@@ -3423,7 +3425,7 @@
@classproperty
- def _insertHomeChildMetaData(cls): #@NoSelf
+ def _insertHomeChildMetaData(cls): #@NoSelf
"""
DAL statement to create a home child with all default values.
"""
@@ -3473,7 +3475,7 @@
@classproperty
- def _metadataByIDQuery(cls): #@NoSelf
+ def _metadataByIDQuery(cls): #@NoSelf
"""
DAL query to retrieve created/modified dates based on a resource ID.
"""
@@ -3554,7 +3556,7 @@
@classproperty
- def _renameQuery(cls): #@NoSelf
+ def _renameQuery(cls): #@NoSelf
"""
DAL statement to rename a L{CommonHomeChild}
"""
@@ -3593,7 +3595,7 @@
@classproperty
- def _deleteQuery(cls): #@NoSelf
+ def _deleteQuery(cls): #@NoSelf
"""
DAL statement to delete a L{CommonHomeChild} by its resource ID.
"""
@@ -3643,7 +3645,7 @@
@classproperty
- def _ownerHomeWithResourceID(cls): #@NoSelf
+ def _ownerHomeWithResourceID(cls): #@NoSelf
"""
DAL query to retrieve the home resource ID and resource name of the owner from the bound
home-child ID.
@@ -3684,7 +3686,7 @@
@classproperty
- def _objectResourceNamesQuery(cls): #@NoSelf
+ def _objectResourceNamesQuery(cls): #@NoSelf
"""
DAL query to load all object resource names for a home child.
"""
@@ -3703,7 +3705,7 @@
@classproperty
- def _objectCountQuery(cls): #@NoSelf
+ def _objectCountQuery(cls): #@NoSelf
"""
DAL query to count all object resources for a home child.
"""
@@ -3769,7 +3771,7 @@
@classproperty
- def _resourceNameForUIDQuery(cls): #@NoSelf
+ def _resourceNameForUIDQuery(cls): #@NoSelf
"""
DAL query to retrieve the resource name for an object resource based on
its UID column.
@@ -3798,7 +3800,7 @@
@classproperty
- def _resourceUIDForNameQuery(cls): #@NoSelf
+ def _resourceUIDForNameQuery(cls): #@NoSelf
"""
DAL query to retrieve the UID for an object resource based on its
resource name column.
@@ -3866,7 +3868,7 @@
@classproperty
- def _moveParentUpdateQuery(cls, adjustName=False): #@NoSelf
+ def _moveParentUpdateQuery(cls, adjustName=False): #@NoSelf
"""
DAL query to update a child to be in a new parent.
"""
@@ -3882,7 +3884,7 @@
)
- def _movedObjectResource(self, child, newparent): #@UnusedVariable
+ def _movedObjectResource(self, child, newparent): #@UnusedVariable
"""
Method that subclasses can override to do an extra DB adjustments when a resource
is moved.
@@ -4102,7 +4104,7 @@
@classproperty
- def _lockLastModifiedQuery(cls): #@NoSelf
+ def _lockLastModifiedQuery(cls): #@NoSelf
schema = cls._homeChildMetaDataSchema
return Select(
From=schema,
@@ -4113,7 +4115,7 @@
@classproperty
- def _changeLastModifiedQuery(cls): #@NoSelf
+ def _changeLastModifiedQuery(cls): #@NoSelf
schema = cls._homeChildMetaDataSchema
return Update({schema.MODIFIED: utcNowSQL},
Where=schema.RESOURCE_ID == Parameter("resourceID"),
@@ -4163,7 +4165,7 @@
BATCH_LOAD_SIZE = 50
- def __init__(self, parent, name, uid, resourceID=None, options=None): #@UnusedVariable
+ def __init__(self, parent, name, uid, resourceID=None, options=None): #@UnusedVariable
self._parentCollection = parent
self._resourceID = resourceID
self._name = name
@@ -4178,7 +4180,7 @@
@classproperty
- def _allColumnsWithParentQuery(cls): #@NoSelf
+ def _allColumnsWithParentQuery(cls): #@NoSelf
obj = cls._objectSchema
return Select(cls._allColumns, From=obj,
Where=obj.PARENT_RESOURCE_ID == Parameter("parentID"))
@@ -4352,17 +4354,17 @@
@classproperty
- def _allColumnsWithParentAndName(cls): #@NoSelf
+ def _allColumnsWithParentAndName(cls): #@NoSelf
return cls._allColumnsWithParentAnd(cls._objectSchema.RESOURCE_NAME, "name")
@classproperty
- def _allColumnsWithParentAndUID(cls): #@NoSelf
+ def _allColumnsWithParentAndUID(cls): #@NoSelf
return cls._allColumnsWithParentAnd(cls._objectSchema.UID, "uid")
@classproperty
- def _allColumnsWithParentAndID(cls): #@NoSelf
+ def _allColumnsWithParentAndID(cls): #@NoSelf
return cls._allColumnsWithParentAnd(cls._objectSchema.RESOURCE_ID, "resourceID")
@@ -4398,7 +4400,7 @@
@classproperty
- def _allColumns(cls): #@NoSelf
+ def _allColumns(cls): #@NoSelf
"""
Full set of columns in the object table that need to be loaded to
initialize the object resource state.
@@ -4497,7 +4499,7 @@
@classmethod
- def _selectForUpdateQuery(cls, nowait): #@NoSelf
+ def _selectForUpdateQuery(cls, nowait): #@NoSelf
"""
DAL statement to lock a L{CommonObjectResource} by its resource ID.
"""
@@ -4541,7 +4543,7 @@
@classproperty
- def _deleteQuery(cls): #@NoSelf
+ def _deleteQuery(cls): #@NoSelf
"""
DAL statement to delete a L{CommonObjectResource} by its resource ID.
"""
@@ -4616,7 +4618,7 @@
@classproperty
- def _textByIDQuery(cls): #@NoSelf
+ def _textByIDQuery(cls): #@NoSelf
"""
DAL query to load iCalendar/vCard text via an object's resource ID.
"""
@@ -4931,7 +4933,7 @@
@classproperty
- def _completelyNewRevisionQuery(cls): #@NoSelf
+ def _completelyNewRevisionQuery(cls): #@NoSelf
rev = cls._revisionsSchema
return Insert({rev.HOME_RESOURCE_ID: Parameter("homeID"),
# rev.RESOURCE_ID: Parameter("resourceID"),
@@ -5003,7 +5005,7 @@
@classproperty
- def _allColumnsByHomeIDQuery(cls): #@NoSelf
+ def _allColumnsByHomeIDQuery(cls): #@NoSelf
"""
DAL query to load all columns by home ID.
"""
@@ -5062,7 +5064,7 @@
@classproperty
- def _oneNotificationQuery(cls): #@NoSelf
+ def _oneNotificationQuery(cls): #@NoSelf
no = cls._objectSchema
return Select(
[
@@ -5103,7 +5105,7 @@
returnValue(None)
- def _loadPropertyStore(self, props=None, created=False): #@UnusedVariable
+ def _loadPropertyStore(self, props=None, created=False): #@UnusedVariable
if props is None:
props = NonePropertyStore(self._home.uid())
self._propertyStore = props
@@ -5141,7 +5143,7 @@
@classproperty
- def _newNotificationQuery(cls): #@NoSelf
+ def _newNotificationQuery(cls): #@NoSelf
no = cls._objectSchema
return Insert(
{
@@ -5156,7 +5158,7 @@
@classproperty
- def _updateNotificationQuery(cls): #@NoSelf
+ def _updateNotificationQuery(cls): #@NoSelf
no = cls._objectSchema
return Update(
{
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2013-07-04 02:29:51 UTC (rev 11460)
@@ -75,7 +75,7 @@
"ALARM_VEVENT_TIMED" nclob default null,
"ALARM_VEVENT_ALLDAY" nclob default null,
"ALARM_VTODO_TIMED" nclob default null,
- "ALARM_VTODO_ALLDAY" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
"TIMEZONE" nclob default null,
primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
@@ -354,12 +354,18 @@
"NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
);
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
create table CALENDARSERVER (
"NAME" nvarchar2(255) primary key,
"VALUE" nvarchar2(255)
);
-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '22');
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '23');
insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql 2013-07-04 02:15:52 UTC (rev 11459)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql 2013-07-04 02:29:51 UTC (rev 11460)
@@ -658,6 +658,16 @@
);
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
--------------------
-- Schema Version --
--------------------
@@ -667,6 +677,6 @@
VALUE varchar(255)
);
-insert into CALENDARSERVER values ('VERSION', '22');
+insert into CALENDARSERVER values ('VERSION', '23');
insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql 2013-07-04 02:29:51 UTC (rev 11460)
@@ -0,0 +1,457 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "DEFAULT_EVENTS" integer default null references CALENDAR on delete set null,
+ "DEFAULT_TASKS" integer default null references CALENDAR on delete set null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+create table SHARED_GROUP_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '22');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+ GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+ TOKEN
+);
+
Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v22.sql 2013-07-04 02:29:51 UTC (rev 11460)
@@ -0,0 +1,667 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks. This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+ LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+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,
+ DEFAULT_EVENTS integer default null references CALENDAR on delete set null,
+ DEFAULT_TASKS integer default null references CALENDAR on delete set null,
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ AVAILABILITY text default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text,
+ TRANSP integer default 0 not null, -- enum CALENDAR_TRANSP
+ ALARM_VEVENT_TIMED text default null,
+ ALARM_VEVENT_ALLDAY text default null,
+ ALARM_VTODO_TIMED text default null,
+ ALARM_VTODO_ALLDAY text default null,
+ TIMEZONE text default null,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+ CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+ SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ KIND integer not null, -- enum ADDRESSBOOK_OBJECT_KIND
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ID integer not null references ADDRESSBOOK_OBJECT, -- member AddressBook Object's RESOURCE_ID
+ primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+
+------------------------------------------
+-- Address Book Object Foreign Members --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+ GROUP_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade, -- AddressBook Object's (kind=='group') RESOURCE_ID
+ ADDRESSBOOK_ID integer not null references ADDRESSBOOK_HOME on delete cascade,
+ MEMBER_ADDRESS varchar(255) not null, -- member AddressBook Object's 'calendar' address
+ primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ GROUP_RESOURCE_ID integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ GROUP_ADDRESSBOOK_RESOURCE_NAME varchar(255) not null,
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ BIND_REVISION integer default 0 not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+ SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- 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),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+ TOKEN varchar(255) not null,
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALUID varchar(255) not null,
+ ACCESSED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+ on IMIP_TOKENS(TOKEN);
+
+
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ FROM_ADDR varchar(255) not null,
+ TO_ADDR varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ ORGANIZER varchar(255) not null,
+ ATTENDEE varchar(255) not null,
+ ICALENDAR_TEXT text not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PUSH_ID varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '22');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_22_to_23.sql 2013-07-04 02:29:51 UTC (rev 11460)
@@ -0,0 +1,32 @@
+----
+-- Copyright (c) 2011-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 22 to 23 --
+---------------------------------------------------
+
+-- Object Splitter Work --
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+
+-- update schema version
+update CALENDARSERVER set VALUE = '23' where NAME = 'VERSION';
Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_22_to_23.sql 2013-07-04 02:29:51 UTC (rev 11460)
@@ -0,0 +1,32 @@
+----
+-- Copyright (c) 2011-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 22 to 23 --
+---------------------------------------------------
+
+-- Object Splitter Work --
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null,
+ NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+
+ -- update schema version
+update CALENDARSERVER set VALUE = '23' where NAME = 'VERSION';
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130703/03efb2f5/attachment-0001.html>
More information about the calendarserver-changes
mailing list