Revision: 9286 http://trac.macosforge.org/projects/calendarserver/changeset/9286 Author: glyph@apple.com Date: 2012-05-24 13:34:09 -0700 (Thu, 24 May 2012) Log Message: ----------- Fix and test for UUID normalization on specific columns rather than dumb uppercasing. Modified Paths: -------------- CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/sql.py CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/test/test_sql.py Modified: CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/sql.py =================================================================== --- CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/sql.py 2012-05-24 20:34:06 UTC (rev 9285) +++ CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/sql.py 2012-05-24 20:34:09 UTC (rev 9286) @@ -85,9 +85,11 @@ from twistedcaldav.dateops import datetimeMktime, parseSQLTimestamp,\ pyCalendarTodatetime from txdav.xml.rfc2518 import DisplayName -from twext.enterprise.dal.syntax import Upper -from twext.enterprise.dal.syntax import Constant +from txdav.base.datastore.util import normalizeUUIDOrNot +from twext.enterprise.dal.syntax import ColumnSyntax +from twext.enterprise.dal.syntax import TableSyntax + from cStringIO import StringIO from sqlparse import parse import collections @@ -4117,20 +4119,50 @@ -def _upcaseColumn(column): +@inlineCallbacks +def _upcaseColumnUUIDs(txn, column): """ - Generate query to upper-case a single column. + Upper-case the UUIDs in the given SQL DAL column. - @param column: the column to uppercase + @param txn: The transaction. + @type txn: L{CommonStoreTransaction} + + @param column: the column to uppercase. Note @type column: L{ColumnSyntax} - @return: a query that, when executed, will cause the given column to become - upper-case according to the database's C{UPPER} function. + @return: A L{Deferred} that will fire when the upper-casing of the given + column has completed. """ - return Update({column: Upper(column)}, Where=Constant(True)) + tableModel = column.model.table + pkey = [ColumnSyntax(columnModel) + for columnModel in tableModel.primaryKey] + for row in (yield Select([column] + pkey, + From=TableSyntax(tableModel)).on(txn)): + before = row[0] + pkeyparts = row[1:] + after = normalizeUUIDOrNot(before) + if after != before: + where = _AndNothing + for pkeycol, pkeypart in zip(pkeyparts, pkey): + where = where.And(pkeycol == pkeypart) + yield Update({column: after}, Where=where).on(txn) +class _AndNothing(object): + """ + Simple placeholder for iteratively generating a 'Where' clause; the 'And' + just returns its argument, so it can be used at the start of the loop. + """ + @staticmethod + def And(self): + """ + Return the argument. + """ + return self + + + @inlineCallbacks def fixCaseNormalization(store): """ @@ -4140,8 +4172,8 @@ try: yield _normalizeHomeUUIDsIn(t, ECALENDARTYPE) yield _normalizeHomeUUIDsIn(t, EADDRESSBOOKTYPE) - yield _upcaseColumn(schema.RESOURCE_PROPERTY.VIEWER_UID).on(t) - yield _upcaseColumn(schema.APN_SUBSCRIPTIONS.SUBSCRIBER_GUID).on(t) + yield _upcaseColumnUUIDs(t, schema.RESOURCE_PROPERTY.VIEWER_UID) + yield _upcaseColumnUUIDs(t, schema.APN_SUBSCRIPTIONS.SUBSCRIBER_GUID) except: log_err() yield t.abort() Modified: CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/test/test_sql.py =================================================================== --- CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/test/test_sql.py 2012-05-24 20:34:06 UTC (rev 9285) +++ CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/test/test_sql.py 2012-05-24 20:34:09 UTC (rev 9286) @@ -31,10 +31,11 @@ CALENDAR_OBJECT_REVISIONS_TABLE from txdav.common.datastore.test.util import CommonCommonTests, buildStore from txdav.common.icommondatastore import AllRetriesFailed +from twext.enterprise.dal.syntax import Insert -class SubTransactionTests(CommonCommonTests, TestCase): +class CommonSQLStoreTests(CommonCommonTests, TestCase): """ - Tests for L{UpgradeToDatabaseService}. + Tests for shared functionality in L{txdav.common.datastore.sql}. """ @inlineCallbacks @@ -42,7 +43,7 @@ """ Set up two stores to migrate between. """ - yield super(SubTransactionTests, self).setUp() + yield super(CommonSQLStoreTests, self).setUp() self._sqlStore = yield buildStore(self, self.notifierFactory) @@ -304,4 +305,33 @@ changed = yield homeChild.resourceNamesSinceToken(token) self.assertEqual(changed, ([], [],)) - txn.abort() + yield txn.abort() + + + @inlineCallbacks + def test_upcaseColumnUUIDs(self): + """ + L{_upcaseColumnUUIDs} upper-cases only UUIDs in a given column. + """ + rp = schema.RESOURCE_PROPERTY + txn = self.transactionUnderTest() + # setup + yield Insert({rp.RESOURCE_ID: 1, + rp.NAME: "asdf", + rp.VALUE: "property-value", + rp.VIEWER_UID: "not-a-uuid"}).on(txn) + yield Insert({rp.RESOURCE_ID: 2, + rp.NAME: "fdsa", + rp.VALUE: "another-value", + rp.VIEWER_UID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"} + ).on(txn) + # test + from txdav.common.datastore.sql import _upcaseColumnUUIDs + yield _upcaseColumnUUIDs(txn, rp.VIEWER_UID) + self.assertEqual( + (yield Select([rp.RESOURCE_ID, rp.NAME, + rp.VALUE, rp.VIEWER_UID], From=rp).on(txn)), + [[1, "asdf", "property-value", "not-a-uuid"], + [2, "fdsa", "another-value", + "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA"]] + )