Revision: 9317 http://trac.macosforge.org/projects/calendarserver/changeset/9317 Author: glyph@apple.com Date: 2012-05-30 17:06:04 -0700 (Wed, 30 May 2012) Log Message: ----------- Test and fix for UUID normalization when there is no de-normalized duplicate. Modified Paths: -------------- CalendarServer/trunk/txdav/common/datastore/sql.py CalendarServer/trunk/txdav/common/datastore/test/test_sql.py Property Changed: ---------------- CalendarServer/trunk/ Modified: CalendarServer/trunk/txdav/common/datastore/sql.py =================================================================== --- CalendarServer/trunk/txdav/common/datastore/sql.py 2012-05-30 20:37:14 UTC (rev 9316) +++ CalendarServer/trunk/txdav/common/datastore/sql.py 2012-05-31 00:06:04 UTC (rev 9317) @@ -4045,25 +4045,48 @@ both.append([other, (yield determineNewest(other.uid(), homeType).on(sqlTxn))]) both.sort(key=lambda x: x[1]) - # Note: determineNewest may return None sometimes. + older = both[0][0] newer = both[1][0] yield migrateHome(older, newer, merge=True) # Rename the old one to 'old.<correct-guid>' newNormalized = normalizeUUIDOrNot(newer.uid()) oldNormalized = normalizeUUIDOrNot(older.uid()) - yield Update({homeTable.OWNER_UID: "old." + oldNormalized}, - Where=homeTable.OWNER_UID == older.uid()).on(sqlTxn) + yield _renameHome(sqlTxn, homeTable, older.uid(), "old." + oldNormalized) # Rename the new one to '<correct-guid>' if newer.uid() != newNormalized: - yield Update( - {homeTable.OWNER_UID: newNormalized}, - Where=homeTable.OWNER_UID == newer.uid() - ).on(sqlTxn) + yield _renameHome(sqlTxn, homeTable, newer.uid(), newNormalized) yield returnValue(newer) +def _renameHome(txn, table, oldUID, newUID): + """ + Rename a calendar, addressbook, or notification home. Note that this + function is only safe in transactions that have had caching disabled, and + more specifically should only ever be used during upgrades. Running this + in a normal transaction will have unpredictable consequences, especially + with respect to memcache. + + @param txn: an SQL transaction to use for this update + @type txn: L{twext.enterprise.ienterprise.IAsyncTransaction} + + @param table: the storage table of the desired home type + @type table: L{TableSyntax} + + @param oldUID: the old UID, the existing home's UID + @type oldUID: L{str} + + @param newUID: the new UID, to change the UID to + @type newUID: L{str} + + @return: a L{Deferred} which fires when the home is renamed. + """ + return Update({table.OWNER_UID: newUID}, + Where=table.OWNER_UID == oldUID).on(txn) + + + def _dontBotherWithNotifications(older, newer, merge): """ Notifications are more transient and can be easily worked around; don't @@ -4137,13 +4160,15 @@ "[%(homeType)s]", uid=UID, newuid=newname, homeType=homeTypeName) other = yield t.homeWithUID(homeType, newname) - if homeType == ECALENDARTYPE: - fixedOtherHome = yield fixOneCalendarHome(other) - if other is not None: + if other is None: + # No duplicate: just fix the name. + yield _renameHome(t, homeTable, UID, newname) + else: + if homeType == ECALENDARTYPE: + fixedOtherHome = yield fixOneCalendarHome(other) this = yield mergeHomes(t, this, other, homeType) - # NOTE: WE MUST NOT TOUCH EITHER HOME OBJECT AFTER THIS - # POINT. THE UIDS HAVE CHANGED AND ALL OPERATIONS WILL - # FAIL. + # NOTE: WE MUST NOT TOUCH EITHER HOME OBJECT AFTER THIS POINT. + # THE UIDS HAVE CHANGED AND ALL OPERATIONS WILL FAIL. end = time.time() elapsed = end - start Modified: CalendarServer/trunk/txdav/common/datastore/test/test_sql.py =================================================================== --- CalendarServer/trunk/txdav/common/datastore/test/test_sql.py 2012-05-30 20:37:14 UTC (rev 9316) +++ CalendarServer/trunk/txdav/common/datastore/test/test_sql.py 2012-05-31 00:06:04 UTC (rev 9317) @@ -21,7 +21,7 @@ from twext.enterprise.dal.syntax import Select from txdav.xml import element as davxml -from twisted.internet.defer import inlineCallbacks +from twisted.internet.defer import inlineCallbacks, returnValue from twisted.internet.task import Clock from twisted.trial.unittest import TestCase @@ -32,6 +32,7 @@ from txdav.common.datastore.test.util import CommonCommonTests, buildStore from txdav.common.icommondatastore import AllRetriesFailed from twext.enterprise.dal.syntax import Insert +from txdav.common.datastore.sql import fixUUIDNormalization class CommonSQLStoreTests(CommonCommonTests, TestCase): """ @@ -335,3 +336,36 @@ [2, "fdsa", "another-value", "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA"]] ) + + + @inlineCallbacks + def allHomeUIDs(self): + """ + Get a listing of all UIDs in the current store. + """ + results = yield Select( + [schema.CALENDAR_HOME.OWNER_UID], + From=schema.CALENDAR_HOME).on(self.transactionUnderTest()) + yield self.commit() + returnValue(results) + + + @inlineCallbacks + def test_fixUUIDNormalization_lowerToUpper(self): + """ + L{fixUUIDNormalization} will fix the normalization of UUIDs. If a home + is found with the wrong case but no duplicate, it will simply be + upper-cased. + """ + t1 = self.transactionUnderTest() + yield t1.calendarHomeWithUID(denormalizedUID, create=True) + yield self.commit() + yield fixUUIDNormalization(self.storeUnderTest()) + self.assertEqual((yield self.allHomeUIDs()), [[normalizedUID]]) + + +from uuid import UUID +exampleUID = UUID("a"*32) +denormalizedUID = str(exampleUID) +normalizedUID = denormalizedUID.upper() +