<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[11860] CalendarServer/trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/11860">11860</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2013-10-31 14:40:44 -0700 (Thu, 31 Oct 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>Allow for incremental upgrade of specific calendar homes.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkcalendarservertapcaldavpy">CalendarServer/trunk/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolsupgradepy">CalendarServer/trunk/calendarserver/tools/upgrade.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavstdconfigpy">CalendarServer/trunk/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoresqlpy">CalendarServer/trunk/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py">CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py">CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_3_to_4py">CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py">CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoreupgradesqlupgradesutilpy">CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/util.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkcalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tap/caldav.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tap/caldav.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -1429,7 +1429,9 @@
</span><span class="cx">
</span><span class="cx"> # Conditionally stop after upgrade at this point
</span><span class="cx"> pps.addStep(
</span><del>- QuitAfterUpgradeStep(config.StopAfterUpgradeTriggerFile)
</del><ins>+ QuitAfterUpgradeStep(
+ config.StopAfterUpgradeTriggerFile or config.UpgradeHomePrefix
+ )
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> pps.addStep(
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolsupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/upgrade.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/upgrade.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/calendarserver/tools/upgrade.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx">
</span><span class="cx"> optParameters = [
</span><span class="cx"> ['config', 'f', DEFAULT_CONFIG_FILE, "Specify caldavd.plist configuration path."],
</span><ins>+ ['prefix', 'x', "", "Only upgrade homes with the specified GUID prefix - partial upgrade only."],
</ins><span class="cx"> ]
</span><span class="cx">
</span><span class="cx"> def __init__(self):
</span><span class="lines">@@ -197,9 +198,11 @@
</span><span class="cx"> data.MergeUpgrades = True
</span><span class="cx"> config.addPostUpdateHooks([setMerge])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def makeService(store):
</span><span class="cx"> return UpgraderService(store, options, output, reactor, config)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def onlyUpgradeEvents(eventDict):
</span><span class="cx"> text = formatEvent(eventDict)
</span><span class="cx"> output.write(logDateString() + " " + text + "\n")
</span><span class="lines">@@ -209,14 +212,19 @@
</span><span class="cx"> log.publisher.levels.setLogLevelForNamespace(None, LogLevel.debug)
</span><span class="cx"> addObserver(onlyUpgradeEvents)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def customServiceMaker():
</span><span class="cx"> customService = CalDAVServiceMaker()
</span><span class="cx"> customService.doPostImport = options["postprocess"]
</span><span class="cx"> return customService
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _patchConfig(config):
</span><span class="cx"> config.FailIfUpgradeNeeded = options["status"]
</span><ins>+ if options["prefix"]:
+ config.UpgradeHomePrefix = options["prefix"]
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def _onShutdown():
</span><span class="cx"> if not UpgraderService.started:
</span><span class="cx"> print("Failed to start service.")
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/stdconfig.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -307,9 +307,15 @@
</span><span class="cx"> "FailIfUpgradeNeeded" : True, # Set to True to prevent the server or utility tools
</span><span class="cx"> # tools from running if the database needs a schema
</span><span class="cx"> # upgrade.
</span><del>- "StopAfterUpgradeTriggerFile" : "stop_after_upgrade", # if this file exists
- # in ConfigRoot, stop the service after finishing upgrade phase
</del><ins>+ "StopAfterUpgradeTriggerFile" : "stop_after_upgrade", # if this file exists in ConfigRoot, stop
+ # the service after finishing upgrade phase
</ins><span class="cx">
</span><ins>+ "UpgradeHomePrefix" : "", # When upgrading, only upgrade homes where the owner UID starts with
+ # with the specified prefix. The upgrade will only be partial and only
+ # apply to upgrade pieces that affect entire homes. The upgrade will
+ # need to be run again without this prefix set to complete the overall
+ # upgrade.
+
</ins><span class="cx"> #
</span><span class="cx"> # Types of service provided
</span><span class="cx"> #
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/sql.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/sql.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -1444,6 +1444,7 @@
</span><span class="cx"> self._txn = transaction
</span><span class="cx"> self._ownerUID = ownerUID
</span><span class="cx"> self._resourceID = None
</span><ins>+ self._dataVersion = None
</ins><span class="cx"> self._childrenLoaded = False
</span><span class="cx"> self._children = {}
</span><span class="cx"> self._notifiers = None
</span><span class="lines">@@ -1689,6 +1690,23 @@
</span><span class="cx"> yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ @classproperty
+ def _dataVersionQuery(cls): #@NoSelf
+ ch = cls._homeSchema
+ return Select(
+ [ch.DATAVERSION], From=ch,
+ Where=ch.RESOURCE_ID == Parameter("resourceID")
+ )
+
+
+ @inlineCallbacks
+ def dataVersion(self):
+ if self._dataVersion is None:
+ self._dataVersion = (yield self._dataVersionQuery.on(
+ self._txn, resourceID=self._resourceID))[0][0]
+ returnValue(self._dataVersion)
+
+
</ins><span class="cx"> def name(self):
</span><span class="cx"> """
</span><span class="cx"> Implement L{IDataStoreObject.name} to return the uid.
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -15,21 +15,17 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from twext.enterprise.dal.syntax import Select, Delete, Parameter
-
</del><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx">
</span><span class="cx"> from twistedcaldav import caldavxml, customxml
</span><span class="cx">
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="cx"> from txdav.caldav.icalendarstore import InvalidDefaultCalendar
</span><del>-from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
-from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty, updateCalendarDataVersion, \
- updateAllCalendarHomeDataVersions, removeProperty, countProperty, cleanPropertyStore, \
- logUpgradeStatus, logUpgradeError
-from txdav.xml.parser import WebDAVDocument
</del><ins>+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.upgrade.sql.upgrades.util import updateCalendarDataVersion, \
+ removeProperty, cleanPropertyStore, logUpgradeStatus, doToEachHomeNotAtVersion
</ins><span class="cx"> from txdav.xml import element
</span><del>-from twisted.python.failure import Failure
</del><ins>+from twistedcaldav.config import config
</ins><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> Data upgrade from database version 3 to 4
</span><span class="lines">@@ -43,212 +39,111 @@
</span><span class="cx"> """
</span><span class="cx"> Do the required upgrade steps.
</span><span class="cx"> """
</span><del>- yield moveDefaultCalendarProperties(sqlStore)
- yield moveCalendarTranspProperties(sqlStore)
- yield moveDefaultAlarmProperties(sqlStore)
- yield removeResourceType(sqlStore)
</del><ins>+ yield updateCalendarHomes(sqlStore, config.UpgradeHomePrefix)
</ins><span class="cx">
</span><del>- # Always bump the DB value
- yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
- yield updateAllCalendarHomeDataVersions(sqlStore, UPGRADE_TO_VERSION)
</del><ins>+ # Don't do remaining upgrade if we are only process a subset of the homes
+ if not config.UpgradeHomePrefix:
+ yield removeResourceType(sqlStore)
</ins><span class="cx">
</span><ins>+ # Always bump the DB value
+ yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def moveDefaultCalendarProperties(sqlStore):
</del><ins>+def updateCalendarHomes(sqlStore, prefix=None):
</ins><span class="cx"> """
</span><del>- Need to move all the CalDAV:default-calendar and CS:default-tasks properties in the
- RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
- the new value from the XML property.
</del><ins>+ For each calendar home, update the associated properties on the home or its owned calendars.
</ins><span class="cx"> """
</span><span class="cx">
</span><del>- meta = schema.CALENDAR_HOME_METADATA
- yield _processDefaultCalendarProperty(sqlStore, caldavxml.ScheduleDefaultCalendarURL, meta.DEFAULT_EVENTS)
- yield _processDefaultCalendarProperty(sqlStore, customxml.ScheduleDefaultTasksURL, meta.DEFAULT_TASKS)
</del><ins>+ yield doToEachHomeNotAtVersion(sqlStore, schema.CALENDAR_HOME, UPGRADE_TO_VERSION, updateCalendarHome, "Update Calendar Home", filterOwnerUID=prefix)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>-def _processDefaultCalendarProperty(sqlStore, propname, colname):
</del><ins>+def updateCalendarHome(txn, homeResourceID):
</ins><span class="cx"> """
</span><del>- Move the specified property value to the matching CALENDAR_HOME_METADATA table column.
-
- Since the number of calendar homes may well be large, we need to do this in batches.
</del><ins>+ For this calendar home, update the associated properties on the home or its owned calendars.
</ins><span class="cx"> """
</span><span class="cx">
</span><del>- cb = schema.CALENDAR_BIND
- rp = schema.RESOURCE_PROPERTY
</del><ins>+ home = yield txn.calendarHomeWithResourceID(homeResourceID)
+ yield moveDefaultCalendarProperties(home)
+ yield moveCalendarTranspProperties(home)
+ yield moveDefaultAlarmProperties(home)
+ yield cleanPropertyStore()
</ins><span class="cx">
</span><del>- logUpgradeStatus("Starting Process {}".format(propname.qname()))
</del><span class="cx">
</span><del>- sqlTxn = sqlStore.newTransaction()
- total = (yield countProperty(sqlTxn, propname))
- yield sqlTxn.commit()
- count = 0
</del><span class="cx">
</span><del>- try:
- inbox_rid = None
- while True:
- sqlTxn = sqlStore.newTransaction()
- rows = (yield rowsForProperty(sqlTxn, propname, batch=BATCH_SIZE))
- if len(rows) == 0:
- yield sqlTxn.commit()
- break
</del><ins>+@inlineCallbacks
+def moveDefaultCalendarProperties(home):
+ """
+ Need to move any the CalDAV:default-calendar and CS:default-tasks properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
+ the new value from the XML property.
+ """
</ins><span class="cx">
</span><del>- delete_ids = []
- for inbox_rid, value in rows:
- delete_ids.append(inbox_rid)
- ids = yield Select(
- [cb.CALENDAR_HOME_RESOURCE_ID, ],
- From=cb,
- Where=cb.CALENDAR_RESOURCE_ID == inbox_rid,
- ).on(sqlTxn)
- if len(ids) > 0:
</del><ins>+ yield _processDefaultCalendarProperty(home, caldavxml.ScheduleDefaultCalendarURL)
+ yield _processDefaultCalendarProperty(home, customxml.ScheduleDefaultTasksURL)
</ins><span class="cx">
</span><del>- calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
- if calendarHome is not None:
</del><span class="cx">
</span><del>- prop = WebDAVDocument.fromString(value).root_element
- defaultCalendar = str(prop.children[0])
- parts = defaultCalendar.split("/")
- if len(parts) == 5:
</del><span class="cx">
</span><del>- calendarName = parts[-1]
- calendarHomeUID = parts[-2]
- expectedHome = (yield sqlTxn.calendarHomeWithUID(calendarHomeUID))
- if expectedHome is not None and expectedHome.id() == calendarHome.id():
</del><ins>+@inlineCallbacks
+def _processDefaultCalendarProperty(home, propname):
+ """
+ Move the specified property value to the matching CALENDAR_HOME_METADATA table column.
+ """
</ins><span class="cx">
</span><del>- calendar = (yield calendarHome.calendarWithName(calendarName))
- if calendar is not None:
- try:
- yield calendarHome.setDefaultCalendar(
- calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
- )
- except InvalidDefaultCalendar:
- # Ignore these - the server will recover
- pass
</del><ins>+ inbox = (yield home.calendarWithName("inbox"))
+ prop = inbox.properties().get(PropertyName.fromElement(propname))
+ if prop is not None:
+ defaultCalendar = str(prop.children[0])
+ parts = defaultCalendar.split("/")
+ if len(parts) == 5:
</ins><span class="cx">
</span><del>- # Always delete the rows so that batch processing works correctly
- yield Delete(
- From=rp,
- Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
- (rp.NAME == PropertyName.fromElement(propname).toString()),
- ).on(sqlTxn, ids=delete_ids)
</del><ins>+ calendarName = parts[-1]
+ calendarHomeUID = parts[-2]
+ if calendarHomeUID == home.uid():
</ins><span class="cx">
</span><del>- yield sqlTxn.commit()
</del><ins>+ calendar = (yield home.calendarWithName(calendarName))
+ if calendar is not None:
+ try:
+ yield home.setDefaultCalendar(
+ calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
+ )
+ except InvalidDefaultCalendar:
+ # Ignore these - the server will recover
+ pass
</ins><span class="cx">
</span><del>- count += len(rows)
- logUpgradeStatus(
- "Process {}".format(propname.qname()),
- count,
- total
- )
</del><ins>+ del inbox.properties()[PropertyName.fromElement(propname)]
</ins><span class="cx">
</span><del>- yield cleanPropertyStore()
- logUpgradeStatus("End Process {}".format(propname.qname()))
</del><span class="cx">
</span><del>- except RuntimeError as e:
- f = Failure()
- logUpgradeError(
- "Process {}".format(propname.qname()),
- "Inbox: {}, error: {}".format(inbox_rid, e),
- )
- yield sqlTxn.abort()
- f.raiseException()
</del><span class="cx">
</span><del>-
-
</del><span class="cx"> @inlineCallbacks
</span><del>-def moveCalendarTranspProperties(sqlStore):
</del><ins>+def moveCalendarTranspProperties(home):
</ins><span class="cx"> """
</span><span class="cx"> Need to move all the CalDAV:schedule-calendar-transp properties in the
</span><span class="cx"> RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
</span><span class="cx"> the new value from the XML property.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- cb = schema.CALENDAR_BIND
- rp = schema.RESOURCE_PROPERTY
</del><ins>+ # Iterate over each calendar (both owned and shared)
+ calendars = (yield home.loadChildren())
+ for calendar in calendars:
+ if calendar.isInbox():
+ continue
+ prop = calendar.properties().get(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp))
+ if prop is not None:
+ yield calendar.setUsedForFreeBusy(prop == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
+ del calendar.properties()[PropertyName.fromElement(caldavxml.ScheduleCalendarTransp)]
+ inbox = (yield home.calendarWithName("inbox"))
+ prop = inbox.properties().get(PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
+ if prop is not None:
+ del inbox.properties()[PropertyName.fromElement(caldavxml.CalendarFreeBusySet)]
</ins><span class="cx">
</span><del>- propname = caldavxml.ScheduleCalendarTransp
- logUpgradeStatus("Starting Process {}".format(propname.qname()))
</del><span class="cx">
</span><del>- sqlTxn = sqlStore.newTransaction()
- total = (yield countProperty(sqlTxn, propname))
- yield sqlTxn.commit()
- count = 0
</del><span class="cx">
</span><del>- try:
- calendar_rid = None
- calendars_for_id = {}
- while True:
- sqlTxn = sqlStore.newTransaction()
- rows = (yield rowsForProperty(sqlTxn, propname, with_uid=True, batch=BATCH_SIZE))
- if len(rows) == 0:
- yield sqlTxn.commit()
- break
-
- delete_ids = []
- for calendar_rid, value, viewer in rows:
- delete_ids.append(calendar_rid)
- if calendar_rid not in calendars_for_id:
- ids = yield Select(
- [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
- From=cb,
- Where=cb.CALENDAR_RESOURCE_ID == calendar_rid,
- ).on(sqlTxn)
- calendars_for_id[calendar_rid] = ids
-
- if viewer:
- calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
- else:
- calendarHome = None
- for row in calendars_for_id[calendar_rid]:
- home_id, bind_mode = row
- if bind_mode == _BIND_MODE_OWN:
- calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
- break
-
- if calendarHome is not None:
- prop = WebDAVDocument.fromString(value).root_element
- calendar = (yield calendarHome.childWithID(calendar_rid))
- if calendar is not None:
- yield calendar.setUsedForFreeBusy(prop == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
-
- # Always delete the rows so that batch processing works correctly
- yield Delete(
- From=rp,
- Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
- (rp.NAME == PropertyName.fromElement(caldavxml.ScheduleCalendarTransp).toString()),
- ).on(sqlTxn, ids=delete_ids)
-
- yield sqlTxn.commit()
-
- count += len(rows)
- logUpgradeStatus(
- "Process {}".format(propname.qname()),
- count,
- total,
- )
-
- sqlTxn = sqlStore.newTransaction()
- yield removeProperty(sqlTxn, PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
- yield sqlTxn.commit()
- yield cleanPropertyStore()
- logUpgradeStatus("End Process {}".format(propname.qname()))
-
- except RuntimeError as e:
- f = Failure()
- logUpgradeError(
- "Process {}".format(propname.qname()),
- "Inbox: {}, error: {}".format(calendar_rid, e),
- )
- yield sqlTxn.abort()
- f.raiseException()
-
-
-
</del><span class="cx"> @inlineCallbacks
</span><del>-def moveDefaultAlarmProperties(sqlStore):
</del><ins>+def moveDefaultAlarmProperties(home):
</ins><span class="cx"> """
</span><span class="cx"> Need to move all the CalDAV:default-calendar and CS:default-tasks properties in the
</span><span class="cx"> RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
</span><span class="lines">@@ -256,25 +151,25 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> yield _processDefaultAlarmProperty(
</span><del>- sqlStore,
</del><ins>+ home,
</ins><span class="cx"> caldavxml.DefaultAlarmVEventDateTime,
</span><span class="cx"> True,
</span><span class="cx"> True,
</span><span class="cx"> )
</span><span class="cx"> yield _processDefaultAlarmProperty(
</span><del>- sqlStore,
</del><ins>+ home,
</ins><span class="cx"> caldavxml.DefaultAlarmVEventDate,
</span><span class="cx"> True,
</span><span class="cx"> False,
</span><span class="cx"> )
</span><span class="cx"> yield _processDefaultAlarmProperty(
</span><del>- sqlStore,
</del><ins>+ home,
</ins><span class="cx"> caldavxml.DefaultAlarmVToDoDateTime,
</span><span class="cx"> False,
</span><span class="cx"> True,
</span><span class="cx"> )
</span><span class="cx"> yield _processDefaultAlarmProperty(
</span><del>- sqlStore,
</del><ins>+ home,
</ins><span class="cx"> caldavxml.DefaultAlarmVToDoDate,
</span><span class="cx"> False,
</span><span class="cx"> False,
</span><span class="lines">@@ -283,108 +178,33 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>-def _processDefaultAlarmProperty(sqlStore, propname, vevent, timed):
</del><ins>+def _processDefaultAlarmProperty(home, propname, vevent, timed):
</ins><span class="cx"> """
</span><span class="cx"> Move the specified property value to the matching CALENDAR_HOME_METADATA or CALENDAR_BIND table column.
</span><span class="cx">
</span><span class="cx"> Since the number of properties may well be large, we need to do this in batches.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- hm = schema.CALENDAR_HOME_METADATA
- cb = schema.CALENDAR_BIND
- rp = schema.RESOURCE_PROPERTY
</del><ins>+ # Check the home first
+ prop = home.properties().get(PropertyName.fromElement(propname))
+ if prop is not None:
+ alarm = str(prop.children[0]) if prop.children else None
+ yield home.setDefaultAlarm(alarm, vevent, timed)
+ del home.properties()[PropertyName.fromElement(propname)]
</ins><span class="cx">
</span><del>- logUpgradeStatus("Starting Process {} {}".format(propname.qname(), vevent))
</del><ins>+ # Now each child
+ calendars = (yield home.loadChildren())
+ for calendar in calendars:
+ if calendar.isInbox():
+ continue
+ prop = calendar.properties().get(PropertyName.fromElement(propname))
+ if prop is not None:
+ alarm = str(prop.children[0]) if prop.children else None
+ yield calendar.setDefaultAlarm(alarm, vevent, timed)
+ del calendar.properties()[PropertyName.fromElement(propname)]
</ins><span class="cx">
</span><del>- sqlTxn = sqlStore.newTransaction()
- total = (yield countProperty(sqlTxn, propname))
- yield sqlTxn.commit()
- count = 0
</del><span class="cx">
</span><del>- try:
- rid = None
- calendars_for_id = {}
- while True:
- sqlTxn = sqlStore.newTransaction()
- rows = (yield rowsForProperty(sqlTxn, propname, with_uid=True, batch=BATCH_SIZE))
- if len(rows) == 0:
- yield sqlTxn.commit()
- break
</del><span class="cx">
</span><del>- delete_ids = []
- for rid, value, viewer in rows:
- delete_ids.append(rid)
-
- prop = WebDAVDocument.fromString(value).root_element
- alarm = str(prop.children[0]) if prop.children else None
-
- # First check if the rid is a home - this is the most common case
- ids = yield Select(
- [hm.RESOURCE_ID, ],
- From=hm,
- Where=hm.RESOURCE_ID == rid,
- ).on(sqlTxn)
-
- if len(ids) > 0:
- # Home object
- calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
- if calendarHome is not None:
- yield calendarHome.setDefaultAlarm(alarm, vevent, timed)
- else:
- # rid is a calendar - we need to find the per-user calendar for the resource viewer
- if rid not in calendars_for_id:
- ids = yield Select(
- [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
- From=cb,
- Where=cb.CALENDAR_RESOURCE_ID == rid,
- ).on(sqlTxn)
- calendars_for_id[rid] = ids
-
- if viewer:
- calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
- else:
- calendarHome = None
- for row in calendars_for_id[rid]:
- home_id, bind_mode = row
- if bind_mode == _BIND_MODE_OWN:
- calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
- break
-
- if calendarHome is not None:
- calendar = yield calendarHome.childWithID(rid)
- if calendar is not None:
- yield calendar.setDefaultAlarm(alarm, vevent, timed)
-
- # Always delete the rows so that batch processing works correctly
- yield Delete(
- From=rp,
- Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
- (rp.NAME == PropertyName.fromElement(propname).toString()),
- ).on(sqlTxn, ids=delete_ids)
-
- yield sqlTxn.commit()
-
- count += len(rows)
- logUpgradeStatus(
- "Process {} {}".format(propname.qname(), vevent),
- count,
- total,
- )
-
- yield cleanPropertyStore()
- logUpgradeStatus("End Process {} {}".format(propname.qname(), vevent))
-
- except RuntimeError as e:
- f = Failure()
- logUpgradeError(
- "Process {} {}".format(propname.qname(), vevent),
- "Rid: {}, error: {}".format(rid, e),
- )
- yield sqlTxn.abort()
- f.raiseException()
-
-
-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def removeResourceType(sqlStore):
</span><span class="cx"> logUpgradeStatus("Starting Calendar Remove Resource Type")
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -15,22 +15,18 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-from twext.enterprise.dal.syntax import Select, Delete, Parameter
</del><ins>+from twext.web2.dav.resource import TwistedQuotaUsedProperty, TwistedGETContentMD5
</ins><span class="cx">
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><del>-from twisted.python.failure import Failure
</del><span class="cx">
</span><span class="cx"> from twistedcaldav import caldavxml, customxml
</span><ins>+from twistedcaldav.config import config
</ins><span class="cx">
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><del>-from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
-from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty, updateCalendarDataVersion, \
- updateAllCalendarHomeDataVersions, removeProperty, cleanPropertyStore, \
- logUpgradeStatus, countProperty, logUpgradeError
</del><ins>+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.upgrade.sql.upgrades.util import updateCalendarDataVersion, \
+ removeProperty, cleanPropertyStore, logUpgradeStatus, doToEachHomeNotAtVersion
</ins><span class="cx"> from txdav.xml import element
</span><del>-from txdav.xml.parser import WebDAVDocument
-from twext.web2.dav.resource import TwistedQuotaUsedProperty, \
- TwistedGETContentMD5
</del><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> Data upgrade from database version 4 to 5
</span><span class="lines">@@ -44,178 +40,75 @@
</span><span class="cx"> """
</span><span class="cx"> Do the required upgrade steps.
</span><span class="cx"> """
</span><del>- yield moveCalendarTimezoneProperties(sqlStore)
- yield moveCalendarAvailabilityProperties(sqlStore)
- yield removeOtherProperties(sqlStore)
</del><ins>+ yield updateCalendarHomes(sqlStore, config.UpgradeHomePrefix)
</ins><span class="cx">
</span><del>- # Always bump the DB value
- yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
- yield updateAllCalendarHomeDataVersions(sqlStore, UPGRADE_TO_VERSION)
</del><ins>+ # Don't do remaining upgrade if we are only process a subset of the homes
+ if not config.UpgradeHomePrefix:
+ yield removeOtherProperties(sqlStore)
</ins><span class="cx">
</span><ins>+ # Always bump the DB value
+ yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def moveCalendarTimezoneProperties(sqlStore):
</del><ins>+def updateCalendarHomes(sqlStore, prefix=None):
</ins><span class="cx"> """
</span><del>- Need to move all the CalDAV:calendar-timezone properties in the
- RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
- the new value from the XML property.
</del><ins>+ For each calendar home, update the associated properties on the home or its owned calendars.
</ins><span class="cx"> """
</span><span class="cx">
</span><del>- cb = schema.CALENDAR_BIND
- rp = schema.RESOURCE_PROPERTY
</del><ins>+ yield doToEachHomeNotAtVersion(sqlStore, schema.CALENDAR_HOME, UPGRADE_TO_VERSION, updateCalendarHome, "Update Calendar Home", filterOwnerUID=prefix)
</ins><span class="cx">
</span><del>- propname = caldavxml.CalendarTimeZone
- logUpgradeStatus("Starting Process {}".format(propname.qname()))
</del><span class="cx">
</span><del>- sqlTxn = sqlStore.newTransaction()
- total = (yield countProperty(sqlTxn, propname))
- yield sqlTxn.commit()
- count = 0
</del><span class="cx">
</span><del>- try:
- calendar_rid = None
- calendars_for_id = {}
- while True:
- sqlTxn = sqlStore.newTransaction()
- rows = (yield rowsForProperty(sqlTxn, propname, with_uid=True, batch=BATCH_SIZE))
- if len(rows) == 0:
- yield sqlTxn.commit()
- break
</del><ins>+@inlineCallbacks
+def updateCalendarHome(txn, homeResourceID):
+ """
+ For this calendar home, update the associated properties on the home or its owned calendars.
+ """
</ins><span class="cx">
</span><del>- delete_ids = []
- for calendar_rid, value, viewer in rows:
- delete_ids.append(calendar_rid)
- if calendar_rid not in calendars_for_id:
- ids = yield Select(
- [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
- From=cb,
- Where=cb.CALENDAR_RESOURCE_ID == calendar_rid,
- ).on(sqlTxn)
- calendars_for_id[calendar_rid] = ids
</del><ins>+ home = yield txn.calendarHomeWithResourceID(homeResourceID)
+ yield moveCalendarTimezoneProperties(home)
+ yield moveCalendarAvailabilityProperties(home)
+ yield cleanPropertyStore()
</ins><span class="cx">
</span><del>- if viewer:
- calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
- else:
- calendarHome = None
- for row in calendars_for_id[calendar_rid]:
- home_id, bind_mode = row
- if bind_mode == _BIND_MODE_OWN:
- calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
- break
</del><span class="cx">
</span><del>- if calendarHome is not None:
- prop = WebDAVDocument.fromString(value).root_element
- calendar = (yield calendarHome.childWithID(calendar_rid))
- if calendar is not None:
- yield calendar.setTimezone(prop.calendar())
</del><span class="cx">
</span><del>- # Always delete the rows so that batch processing works correctly
- yield Delete(
- From=rp,
- Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
- (rp.NAME == PropertyName.fromElement(caldavxml.CalendarTimeZone).toString()),
- ).on(sqlTxn, ids=delete_ids)
</del><ins>+@inlineCallbacks
+def moveCalendarTimezoneProperties(home):
+ """
+ Need to move all the CalDAV:calendar-timezone properties in the
+ RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
+ the new value from the XML property.
+ """
</ins><span class="cx">
</span><del>- yield sqlTxn.commit()
- count += len(rows)
- logUpgradeStatus(
- "Process {}".format(propname.qname()),
- count,
- total,
- )
</del><ins>+ # Iterate over each calendar (both owned and shared)
+ calendars = (yield home.loadChildren())
+ for calendar in calendars:
+ if calendar.isInbox():
+ continue
+ prop = calendar.properties().get(PropertyName.fromElement(caldavxml.CalendarTimeZone))
+ if prop is not None:
+ yield calendar.setTimezone(prop.calendar())
+ del calendar.properties()[PropertyName.fromElement(caldavxml.CalendarTimeZone)]
</ins><span class="cx">
</span><del>- yield cleanPropertyStore()
- logUpgradeStatus("End Process {}".format(propname.qname()))
</del><span class="cx">
</span><del>- except RuntimeError as e:
- f = Failure()
- logUpgradeError(
- "Process {}".format(propname.qname()),
- "Rid: {}, error: {}".format(calendar_rid, e),
- )
- yield sqlTxn.abort()
- f.raiseException()
</del><span class="cx">
</span><del>-
-
</del><span class="cx"> @inlineCallbacks
</span><del>-def moveCalendarAvailabilityProperties(sqlStore):
</del><ins>+def moveCalendarAvailabilityProperties(home):
</ins><span class="cx"> """
</span><span class="cx"> Need to move all the CS:calendar-availability properties in the
</span><span class="cx"> RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
</span><span class="cx"> the new value from the XML property.
</span><span class="cx"> """
</span><ins>+ inbox = (yield home.calendarWithName("inbox"))
+ prop = inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability))
+ if prop is not None:
+ yield home.setAvailability(prop.calendar())
+ del inbox.properties()[customxml.CalendarAvailability]
</ins><span class="cx">
</span><del>- cb = schema.CALENDAR_BIND
- rp = schema.RESOURCE_PROPERTY
</del><span class="cx">
</span><del>- propname = customxml.CalendarAvailability
- logUpgradeStatus("Starting Process {}".format(propname.qname()))
</del><span class="cx">
</span><del>- sqlTxn = sqlStore.newTransaction()
- total = (yield countProperty(sqlTxn, propname))
- yield sqlTxn.commit()
- count = 0
-
- try:
- calendar_rid = None
- while True:
- sqlTxn = sqlStore.newTransaction()
- rows = (yield rowsForProperty(sqlTxn, propname, batch=BATCH_SIZE))
- if len(rows) == 0:
- yield sqlTxn.commit()
- break
-
- # Map each calendar to a home id using a single query for efficiency
- calendar_ids = [row[0] for row in rows]
-
- home_map = yield Select(
- [cb.CALENDAR_RESOURCE_ID, cb.CALENDAR_HOME_RESOURCE_ID, ],
- From=cb,
- Where=(cb.CALENDAR_RESOURCE_ID.In(Parameter("ids", len(calendar_ids)))).And(cb.BIND_MODE == _BIND_MODE_OWN),
- ).on(sqlTxn, ids=calendar_ids)
- calendar_to_home = dict(home_map)
-
- # Move property to each home
- for calendar_rid, value in rows:
- if calendar_rid in calendar_to_home:
- calendarHome = (yield sqlTxn.calendarHomeWithResourceID(calendar_to_home[calendar_rid]))
-
- if calendarHome is not None:
- prop = WebDAVDocument.fromString(value).root_element
- yield calendarHome.setAvailability(prop.calendar())
-
- # Always delete the rows so that batch processing works correctly
- yield Delete(
- From=rp,
- Where=(rp.RESOURCE_ID.In(Parameter("ids", len(calendar_ids)))).And
- (rp.NAME == PropertyName.fromElement(customxml.CalendarAvailability).toString()),
- ).on(sqlTxn, ids=calendar_ids)
-
- yield sqlTxn.commit()
-
- count += len(rows)
- logUpgradeStatus(
- "Process {}".format(propname.qname()),
- count,
- total,
- )
-
- yield cleanPropertyStore()
- logUpgradeStatus("End Process {}".format(propname.qname()))
-
- except RuntimeError as e:
- f = Failure()
- logUpgradeError(
- "Process {}".format(propname.qname()),
- "Rid: {}, error: {}".format(calendar_rid, e),
- )
- yield sqlTxn.abort()
- f.raiseException()
-
-
-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def removeOtherProperties(sqlStore):
</span><span class="cx"> """
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_3_to_4py"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -13,23 +13,27 @@
</span><span class="cx"> # See the License for the specific language governing permissions and
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><ins>+
+from twext.enterprise.dal.syntax import Update, Insert
+
+from twistedcaldav import caldavxml
</ins><span class="cx"> from twistedcaldav.caldavxml import ScheduleDefaultCalendarURL, \
</span><del>- CalendarFreeBusySet, Opaque, ScheduleCalendarTransp
</del><ins>+ CalendarFreeBusySet, Opaque, ScheduleCalendarTransp, Transparent
+
</ins><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="cx"> from txdav.caldav.datastore.test.util import CommonStoreTests
</span><ins>+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
+from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_3_to_4 import updateCalendarHomes, \
+ doUpgrade
+from txdav.xml import element
</ins><span class="cx"> from txdav.xml.element import HRef
</span><del>-from twext.enterprise.dal.syntax import Update, Insert
-from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_3_to_4 import moveDefaultCalendarProperties, \
- moveCalendarTranspProperties, removeResourceType, moveDefaultAlarmProperties
-from txdav.xml import element
-from twistedcaldav import caldavxml
-from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
</del><ins>+from twistedcaldav.config import config
</ins><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from twisted.internet.defer import inlineCallbacks
</del><ins>+from twisted.internet.defer import inlineCallbacks, returnValue
</ins><span class="cx">
</span><span class="cx"> class Upgrade_from_3_to_4(CommonStoreTests):
</span><span class="cx"> """
</span><span class="lines">@@ -37,7 +41,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_defaultCalendarUpgrade(self):
</del><ins>+ def _defaultCalendarUpgrade_setup(self):
</ins><span class="cx">
</span><span class="cx"> # Set dead property on inbox
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="lines">@@ -52,30 +56,56 @@
</span><span class="cx"> Where=chm.RESOURCE_ID == home._resourceID,
</span><span class="cx"> ).on(self.transactionUnderTest())
</span><span class="cx">
</span><del>- # Force data version to previous
- ch = home._homeSchema
- yield Update(
- {ch.DATAVERSION: 3},
- Where=ch.RESOURCE_ID == home._resourceID,
- ).on(self.transactionUnderTest())
</del><ins>+ # Force data version to previous
+ ch = home._homeSchema
+ yield Update(
+ {ch.DATAVERSION: 3},
+ Where=ch.RESOURCE_ID == home._resourceID,
+ ).on(self.transactionUnderTest())
</ins><span class="cx">
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield moveDefaultCalendarProperties(self._sqlCalendarStore)
</del><span class="cx">
</span><ins>+ @inlineCallbacks
+ def _defaultCalendarUpgrade_check(self, changed_users, unchanged_users):
+
</ins><span class="cx"> # Test results
</span><del>- for user in ("user01", "user02",):
</del><ins>+ for user in changed_users:
</ins><span class="cx"> home = (yield self.homeUnderTest(name=user))
</span><ins>+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
</ins><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="cx"> self.assertTrue(home.isDefaultCalendar(calendar))
</span><span class="cx"> inbox = (yield self.calendarUnderTest(name="inbox", home=user))
</span><span class="cx"> self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) not in inbox.properties())
</span><span class="cx">
</span><ins>+ for user in unchanged_users:
+ home = (yield self.homeUnderTest(name=user))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 3)
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertFalse(home.isDefaultCalendar(calendar))
+ inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+ self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) in inbox.properties())
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def test_invalidDefaultCalendarUpgrade(self):
</del><ins>+ def test_defaultCalendarUpgrade(self):
+ yield self._defaultCalendarUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._defaultCalendarUpgrade_check(("user01", "user02",), ())
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialDefaultCalendarUpgrade(self):
+ yield self._defaultCalendarUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._defaultCalendarUpgrade_check(("user01",), ("user02",))
+
+
+ @inlineCallbacks
+ def _invalidDefaultCalendarUpgrade_setup(self):
+
</ins><span class="cx"> # Set dead property on inbox
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> inbox = (yield self.calendarUnderTest(name="inbox", home=user))
</span><span class="lines">@@ -93,30 +123,56 @@
</span><span class="cx"> tasks = (yield home.createCalendarWithName("tasks_1"))
</span><span class="cx"> yield tasks.setSupportedComponents("VTODO")
</span><span class="cx">
</span><del>- # Force data version to previous
- ch = home._homeSchema
- yield Update(
- {ch.DATAVERSION: 3},
- Where=ch.RESOURCE_ID == home._resourceID,
- ).on(self.transactionUnderTest())
</del><ins>+ # Force data version to previous
+ ch = home._homeSchema
+ yield Update(
+ {ch.DATAVERSION: 3},
+ Where=ch.RESOURCE_ID == home._resourceID,
+ ).on(self.transactionUnderTest())
</ins><span class="cx">
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield moveDefaultCalendarProperties(self._sqlCalendarStore)
</del><span class="cx">
</span><ins>+ @inlineCallbacks
+ def _invalidDefaultCalendarUpgrade_check(self, changed_users, unchanged_users):
+
</ins><span class="cx"> # Test results
</span><del>- for user in ("user01", "user02",):
</del><ins>+ for user in changed_users:
</ins><span class="cx"> home = (yield self.homeUnderTest(name=user))
</span><ins>+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
</ins><span class="cx"> calendar = (yield self.calendarUnderTest(name="tasks_1", home=user))
</span><span class="cx"> self.assertFalse(home.isDefaultCalendar(calendar))
</span><span class="cx"> inbox = (yield self.calendarUnderTest(name="inbox", home=user))
</span><span class="cx"> self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) not in inbox.properties())
</span><span class="cx">
</span><ins>+ for user in unchanged_users:
+ home = (yield self.homeUnderTest(name=user))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 3)
+ calendar = (yield self.calendarUnderTest(name="tasks_1", home=user))
+ self.assertFalse(home.isDefaultCalendar(calendar))
+ inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+ self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) in inbox.properties())
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def test_calendarTranspUpgrade(self):
</del><ins>+ def test_invalidDefaultCalendarUpgrade(self):
+ yield self._invalidDefaultCalendarUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._invalidDefaultCalendarUpgrade_check(("user01", "user02",), ())
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialInvalidDefaultCalendarUpgrade(self):
+ yield self._invalidDefaultCalendarUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._invalidDefaultCalendarUpgrade_check(("user01",), ("user02",))
+
+
+ @inlineCallbacks
+ def _calendarTranspUpgrade_setup(self):
+
</ins><span class="cx"> # Set dead property on inbox
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> inbox = (yield self.calendarUnderTest(name="inbox", home=user))
</span><span class="lines">@@ -125,7 +181,7 @@
</span><span class="cx"> # Force current to transparent
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="cx"> yield calendar.setUsedForFreeBusy(False)
</span><del>- calendar.properties()[PropertyName.fromElement(ScheduleCalendarTransp)] = ScheduleCalendarTransp(Opaque())
</del><ins>+ calendar.properties()[PropertyName.fromElement(ScheduleCalendarTransp)] = ScheduleCalendarTransp(Opaque() if user == "user01" else Transparent())
</ins><span class="cx">
</span><span class="cx"> # Force data version to previous
</span><span class="cx"> home = (yield self.homeUnderTest(name=user))
</span><span class="lines">@@ -159,21 +215,55 @@
</span><span class="cx"> ).on(txn)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield moveCalendarTranspProperties(self._sqlCalendarStore)
</del><span class="cx">
</span><ins>+ @inlineCallbacks
+ def _calendarTranspUpgrade_check(self, changed_users, unchanged_users):
+
</ins><span class="cx"> # Test results
</span><del>- for user in ("user01", "user02",):
</del><ins>+ for user in changed_users:
</ins><span class="cx"> home = (yield self.homeUnderTest(name=user))
</span><ins>+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
</ins><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><del>- self.assertTrue(calendar.isUsedForFreeBusy())
</del><ins>+ if user == "user01":
+ self.assertTrue(calendar.isUsedForFreeBusy())
+ else:
+ self.assertFalse(calendar.isUsedForFreeBusy())
+ self.assertTrue(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp) not in calendar.properties())
</ins><span class="cx"> inbox = (yield self.calendarUnderTest(name="inbox", home=user))
</span><span class="cx"> self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) not in inbox.properties())
</span><span class="cx">
</span><ins>+ for user in unchanged_users:
+ home = (yield self.homeUnderTest(name=user))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 3)
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ if user == "user01":
+ self.assertFalse(calendar.isUsedForFreeBusy())
+ else:
+ self.assertFalse(calendar.isUsedForFreeBusy())
+ self.assertTrue(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp) in calendar.properties())
+ inbox = (yield self.calendarUnderTest(name="inbox", home=user))
+ self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) in inbox.properties())
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def test_defaultAlarmUpgrade(self):
</del><ins>+ def test_calendarTranspUpgrade(self):
+ yield self._calendarTranspUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._calendarTranspUpgrade_check(("user01", "user02",), ())
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialCalendarTranspUpgrade(self):
+ yield self._calendarTranspUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarTranspUpgrade_check(("user01",), ("user02",))
+
+
+ @inlineCallbacks
+ def _defaultAlarmUpgrade_setup(self):
+
</ins><span class="cx"> alarmhome1 = """BEGIN:VALARM
</span><span class="cx"> ACTION:AUDIO
</span><span class="cx"> TRIGGER;RELATED=START:-PT1M
</span><span class="lines">@@ -277,13 +367,28 @@
</span><span class="cx"> shared = yield self.calendarUnderTest(name=shared_name, home="user02")
</span><span class="cx"> for _ignore_vevent, _ignore_timed, alarm, prop in detailsshared:
</span><span class="cx"> shared.properties()[PropertyName.fromElement(prop)] = prop(alarm)
</span><ins>+
+ for user in ("user01", "user02",):
+ # Force data version to previous
+ home = (yield self.homeUnderTest(name=user))
+ ch = home._homeSchema
+ yield Update(
+ {ch.DATAVERSION: 3},
+ Where=ch.RESOURCE_ID == home._resourceID,
+ ).on(self.transactionUnderTest())
+
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield moveDefaultAlarmProperties(self._sqlCalendarStore)
</del><ins>+ returnValue((detailshome, detailscalendar, detailsshared, shared_name,))
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def _defaultAlarmUpgrade_check(self, changed_users, unchanged_users, detailshome, detailscalendar, detailsshared, shared_name):
+
</ins><span class="cx"> # Check each type of collection
</span><span class="cx"> home = yield self.homeUnderTest(name="user01")
</span><ins>+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
</ins><span class="cx"> for vevent, timed, alarm, prop in detailshome:
</span><span class="cx"> alarm_result = (yield home.getDefaultAlarm(vevent, timed))
</span><span class="cx"> self.assertEquals(alarm_result, alarm)
</span><span class="lines">@@ -293,18 +398,67 @@
</span><span class="cx"> for vevent, timed, alarm, prop in detailscalendar:
</span><span class="cx"> alarm_result = (yield calendar.getDefaultAlarm(vevent, timed))
</span><span class="cx"> self.assertEquals(alarm_result, alarm)
</span><del>- self.assertTrue(PropertyName.fromElement(prop) not in home.properties())
</del><ins>+ self.assertTrue(PropertyName.fromElement(prop) not in calendar.properties())
</ins><span class="cx">
</span><del>- shared = yield self.calendarUnderTest(name=shared_name, home="user02")
- for vevent, timed, alarm, prop in detailsshared:
- alarm_result = (yield shared.getDefaultAlarm(vevent, timed))
- self.assertEquals(alarm_result, alarm)
- self.assertTrue(PropertyName.fromElement(prop) not in home.properties())
</del><ins>+ if "user02" in changed_users:
+ home = (yield self.homeUnderTest(name="user02"))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
+ shared = yield self.calendarUnderTest(name=shared_name, home="user02")
+ for vevent, timed, alarm, prop in detailsshared:
+ alarm_result = (yield shared.getDefaultAlarm(vevent, timed))
+ self.assertEquals(alarm_result, alarm)
+ self.assertTrue(PropertyName.fromElement(prop) not in shared.properties())
+ else:
+ home = (yield self.homeUnderTest(name="user02"))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 3)
+ shared = yield self.calendarUnderTest(name=shared_name, home="user02")
+ for vevent, timed, alarm, prop in detailsshared:
+ alarm_result = (yield shared.getDefaultAlarm(vevent, timed))
+ self.assertEquals(alarm_result, None)
+ self.assertTrue(PropertyName.fromElement(prop) in shared.properties())
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_resourceTypeUpgrade(self):
</del><ins>+ def test_defaultAlarmUpgrade(self):
+ detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._defaultAlarmUpgrade_check(("user01", "user02",), (), detailshome, detailscalendar, detailsshared, shared_name)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialDefaultAlarmUpgrade(self):
+ detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._defaultAlarmUpgrade_check(("user01",), ("user02",), detailshome, detailscalendar, detailsshared, shared_name)
+
+
+ @inlineCallbacks
+ def test_combinedUpgrade(self):
+ yield self._defaultCalendarUpgrade_setup()
+ yield self._calendarTranspUpgrade_setup()
+ detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._defaultCalendarUpgrade_check(("user01", "user02",), ())
+ yield self._calendarTranspUpgrade_check(("user01", "user02",), ())
+ yield self._defaultAlarmUpgrade_check(("user01", "user02",), (), detailshome, detailscalendar, detailsshared, shared_name)
+
+
+ @inlineCallbacks
+ def test_partialCombinedUpgrade(self):
+ yield self._defaultCalendarUpgrade_setup()
+ yield self._calendarTranspUpgrade_setup()
+ detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._defaultCalendarUpgrade_check(("user01",), ("user02",))
+ yield self._calendarTranspUpgrade_check(("user01",), ("user02",))
+ yield self._defaultAlarmUpgrade_check(("user01",), ("user02",), detailshome, detailscalendar, detailsshared, shared_name)
+
+
+ @inlineCallbacks
+ def _resourceTypeUpgrade_setup(self):
+
</ins><span class="cx"> # Set dead property on calendar
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="lines">@@ -314,12 +468,60 @@
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="cx"> self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
</span><ins>+
+ yield self.transactionUnderTest().updateCalendarserverValue("CALENDAR-DATAVERSION", "3")
+
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield removeResourceType(self._sqlCalendarStore)
</del><span class="cx">
</span><ins>+ @inlineCallbacks
+ def _resourceTypeUpgrade_check(self, full=True):
+
</ins><span class="cx"> # Test results
</span><del>- for user in ("user01", "user02",):
- calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
- self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())
</del><ins>+ if full:
+ for user in ("user01", "user02",):
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ self.assertEqual(int(version), 4)
+ else:
+ for user in ("user01", "user02",):
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ self.assertEqual(int(version), 3)
+
+
+ @inlineCallbacks
+ def test_resourceTypeUpgrade(self):
+ yield self._resourceTypeUpgrade_setup()
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._resourceTypeUpgrade_check()
+
+
+ @inlineCallbacks
+ def test_fullUpgrade(self):
+ self.patch(config, "UpgradeHomePrefix", "")
+ yield self._defaultCalendarUpgrade_setup()
+ yield self._calendarTranspUpgrade_setup()
+ detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+ yield self._resourceTypeUpgrade_setup()
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._defaultCalendarUpgrade_check(("user01", "user02",), ())
+ yield self._calendarTranspUpgrade_check(("user01", "user02",), ())
+ yield self._defaultAlarmUpgrade_check(("user01", "user02",), (), detailshome, detailscalendar, detailsshared, shared_name)
+ yield self._resourceTypeUpgrade_check()
+
+
+ @inlineCallbacks
+ def test_partialFullUpgrade(self):
+ self.patch(config, "UpgradeHomePrefix", "user01")
+ yield self._defaultCalendarUpgrade_setup()
+ yield self._calendarTranspUpgrade_setup()
+ yield self._resourceTypeUpgrade_setup()
+ detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._defaultCalendarUpgrade_check(("user01",), ("user02",))
+ yield self._calendarTranspUpgrade_check(("user01",), ("user02",))
+ yield self._defaultAlarmUpgrade_check(("user01",), ("user02",), detailshome, detailscalendar, detailsshared, shared_name)
+ yield self._resourceTypeUpgrade_check(False)
</ins></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -13,21 +13,24 @@
</span><span class="cx"> # See the License for the specific language governing permissions and
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><del>-from twistedcaldav import caldavxml, customxml
-from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_4_to_5 import moveCalendarTimezoneProperties, \
- removeOtherProperties, moveCalendarAvailabilityProperties
-from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
-from txdav.xml import element
</del><span class="cx">
</span><span class="cx"> """
</span><span class="cx"> Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> from twext.enterprise.dal.syntax import Update, Insert
</span><del>-from twisted.internet.defer import inlineCallbacks
</del><ins>+
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+from twistedcaldav import caldavxml, customxml
+from twistedcaldav.config import config
</ins><span class="cx"> from twistedcaldav.ical import Component
</span><ins>+
</ins><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="cx"> from txdav.caldav.datastore.test.util import CommonStoreTests
</span><ins>+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
+from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_4_to_5 import updateCalendarHomes, doUpgrade
+from txdav.xml import element
</ins><span class="cx">
</span><span class="cx"> class Upgrade_from_4_to_5(CommonStoreTests):
</span><span class="cx"> """
</span><span class="lines">@@ -35,7 +38,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_calendarTimezoneUpgrade(self):
</del><ins>+ def _calendarTimezoneUpgrade_setup(self):
</ins><span class="cx">
</span><span class="cx"> tz1 = Component.fromString("""BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="lines">@@ -137,19 +140,47 @@
</span><span class="cx"> ).on(txn)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield moveCalendarTimezoneProperties(self._sqlCalendarStore)
</del><ins>+ returnValue(user_details)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def _calendarTimezoneUpgrade_check(self, changed_users, unchanged_users, user_details):
+
</ins><span class="cx"> # Test results
</span><span class="cx"> for user, calname, tz in user_details:
</span><del>- calendar = (yield self.calendarUnderTest(name=calname, home=user))
- self.assertEqual(calendar.getTimezone(), tz)
- self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
</del><ins>+ if user in changed_users:
+ home = (yield self.homeUnderTest(name=user))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 5)
+ calendar = (yield self.calendarUnderTest(name=calname, home=user))
+ self.assertEqual(calendar.getTimezone(), tz)
+ self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
+ else:
+ home = (yield self.homeUnderTest(name=user))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
+ calendar = (yield self.calendarUnderTest(name=calname, home=user))
+ self.assertEqual(calendar.getTimezone(), None)
+ self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties())
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_calendarAvailabilityUpgrade(self):
</del><ins>+ def test_calendarTimezoneUpgrade(self):
+ user_details = yield self._calendarTimezoneUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._calendarTimezoneUpgrade_check(("user01", "user02", "user03",), (), user_details)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialCalendarTimezoneUpgrade(self):
+ user_details = yield self._calendarTimezoneUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarTimezoneUpgrade_check(("user01",), ("user02", "user03",), user_details)
+
+
+ @inlineCallbacks
+ def _calendarAvailabilityUpgrade_setup(self):
+
</ins><span class="cx"> av1 = Component.fromString("""BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> CALSCALE:GREGORIAN
</span><span class="lines">@@ -220,20 +251,65 @@
</span><span class="cx"> self.assertEqual(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties(), av is not None)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield moveCalendarAvailabilityProperties(self._sqlCalendarStore)
</del><ins>+ returnValue(user_details)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def _calendarAvailabilityUpgrade_check(self, changed_users, unchanged_users, user_details):
+
</ins><span class="cx"> # Test results
</span><span class="cx"> for user, av in user_details:
</span><del>- home = (yield self.homeUnderTest(name=user))
- calendar = (yield self.calendarUnderTest(name="inbox", home=user))
- self.assertEqual(home.getAvailability(), av)
- self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties())
</del><ins>+ if user in changed_users:
+ home = (yield self.homeUnderTest(name=user))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 5)
+ calendar = (yield self.calendarUnderTest(name="inbox", home=user))
+ self.assertEqual(home.getAvailability(), av)
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties())
+ else:
+ home = (yield self.homeUnderTest(name=user))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
+ calendar = (yield self.calendarUnderTest(name="inbox", home=user))
+ self.assertEqual(home.getAvailability(), None)
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties())
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def test_removeOtherPropertiesUpgrade(self):
</del><ins>+ def test_calendarAvailabilityUpgrade(self):
+ user_details = yield self._calendarAvailabilityUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._calendarAvailabilityUpgrade_check(("user01", "user02", "user03",), (), user_details)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialCalendarAvailabilityUpgrade(self):
+ user_details = yield self._calendarAvailabilityUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarAvailabilityUpgrade_check(("user01",), ("user02", "user03",), user_details)
+
+
+ @inlineCallbacks
+ def test_combinedUpgrade(self):
+ user_details1 = yield self._calendarTimezoneUpgrade_setup()
+ user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore)
+ yield self._calendarTimezoneUpgrade_check(("user01", "user02", "user03",), (), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01", "user02", "user03",), (), user_details2)
+
+
+ @inlineCallbacks
+ def test_partialCombinedUpgrade(self):
+ user_details1 = yield self._calendarTimezoneUpgrade_setup()
+ user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarTimezoneUpgrade_check(("user01",), ("user02", "user03",), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01",), ("user02", "user03",), user_details2)
+
+
+ @inlineCallbacks
+ def _removeOtherPropertiesUpgrade_setup(self):
+
</ins><span class="cx"> # Set dead property on calendar
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="lines">@@ -243,12 +319,55 @@
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="cx"> self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties())
</span><ins>+
+ yield self.transactionUnderTest().updateCalendarserverValue("CALENDAR-DATAVERSION", "4")
+
</ins><span class="cx"> yield self.commit()
</span><span class="cx">
</span><del>- # Trigger upgrade
- yield removeOtherProperties(self._sqlCalendarStore)
</del><span class="cx">
</span><ins>+ @inlineCallbacks
+ def _removeOtherPropertiesUpgrade_check(self, full=True):
+
</ins><span class="cx"> # Test results
</span><span class="cx"> for user in ("user01", "user02",):
</span><del>- calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
- self.assertTrue(PropertyName.fromElement(element.ResourceID) not in calendar.properties())
</del><ins>+ if full:
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceID) not in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ self.assertEqual(int(version), 5)
+ else:
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ self.assertEqual(int(version), 4)
+
+
+ @inlineCallbacks
+ def test_removeOtherPropertiesUpgrade(self):
+ yield self._removeOtherPropertiesUpgrade_setup()
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._removeOtherPropertiesUpgrade_check()
+
+
+ @inlineCallbacks
+ def test_fullUpgrade(self):
+ self.patch(config, "UpgradeHomePrefix", "")
+ user_details1 = yield self._calendarTimezoneUpgrade_setup()
+ user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+ yield self._removeOtherPropertiesUpgrade_setup()
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._calendarTimezoneUpgrade_check(("user01", "user02", "user03",), (), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01", "user02", "user03",), (), user_details2)
+ yield self._removeOtherPropertiesUpgrade_check()
+
+
+ @inlineCallbacks
+ def test_partialFullUpgrade(self):
+ self.patch(config, "UpgradeHomePrefix", "user01")
+ user_details1 = yield self._calendarTimezoneUpgrade_setup()
+ user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+ yield self._removeOtherPropertiesUpgrade_setup()
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._calendarTimezoneUpgrade_check(("user01",), ("user02", "user03",), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01",), ("user02", "user03",), user_details2)
+ yield self._removeOtherPropertiesUpgrade_check(False)
</ins></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoreupgradesqlupgradesutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/util.py (11859 => 11860)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/util.py        2013-10-31 18:37:58 UTC (rev 11859)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrades/utilpy        2013-10-31 21:40:44 UTC (rev 11860)
</span><span class="lines">@@ -129,17 +129,21 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>-def doToEachHomeNotAtVersion(store, homeSchema, version, doIt, logStr):
</del><ins>+def doToEachHomeNotAtVersion(store, homeSchema, version, doIt, logStr, filterOwnerUID=None):
</ins><span class="cx"> """
</span><span class="cx"> Do something to each home whose version column indicates it is older
</span><del>- than the specified version. Do this in batches as there may be a lot of work to do.
</del><ins>+ than the specified version. Do this in batches as there may be a lot of work to do. Also,
+ allow the GUID to be filtered to support a parallel mode of operation.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction("updateDataVersion")
</span><ins>+ where = homeSchema.DATAVERSION < version
+ if filterOwnerUID:
+ where = where.And(homeSchema.OWNER_UID.StartsWith(filterOwnerUID))
</ins><span class="cx"> total = (yield Select(
</span><span class="cx"> [Count(homeSchema.RESOURCE_ID), ],
</span><span class="cx"> From=homeSchema,
</span><del>- Where=homeSchema.DATAVERSION < version,
</del><ins>+ Where=where,
</ins><span class="cx"> ).on(txn))[0][0]
</span><span class="cx"> yield txn.commit()
</span><span class="cx"> count = 0
</span><span class="lines">@@ -154,7 +158,7 @@
</span><span class="cx"> rows = yield Select(
</span><span class="cx"> [homeSchema.RESOURCE_ID, homeSchema.OWNER_UID, ],
</span><span class="cx"> From=homeSchema,
</span><del>- Where=homeSchema.DATAVERSION < version,
</del><ins>+ Where=where,
</ins><span class="cx"> OrderBy=homeSchema.OWNER_UID,
</span><span class="cx"> Limit=1,
</span><span class="cx"> ).on(txn)
</span></span></pre>
</div>
</div>
</body>
</html>