[CalendarServer-changes] [10027] CalendarServer/branches/users/cdaboo/managed-attachments
source_changes at macosforge.org
source_changes at macosforge.org
Tue Nov 13 13:40:20 PST 2012
Revision: 10027
http://trac.calendarserver.org//changeset/10027
Author: cdaboo at apple.com
Date: 2012-11-13 13:40:20 -0800 (Tue, 13 Nov 2012)
Log Message:
-----------
Schema change to support multiple managed-ids pointing to the same underlying attachment.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/managed-attachments/twistedcaldav/storebridge.py
CalendarServer/branches/users/cdaboo/managed-attachments/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/current.sql
CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_12_to_13.sql
Modified: CalendarServer/branches/users/cdaboo/managed-attachments/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/managed-attachments/twistedcaldav/storebridge.py 2012-11-13 17:07:03 UTC (rev 10026)
+++ CalendarServer/branches/users/cdaboo/managed-attachments/twistedcaldav/storebridge.py 2012-11-13 21:40:20 UTC (rev 10027)
@@ -1430,6 +1430,7 @@
self._newStoreCalendarObject,
attachment,
name,
+ False,
principalCollections=self.principalCollections()
)
self.propagateTransaction(result)
@@ -1644,11 +1645,12 @@
@inlineCallbacks
def getChild(self, name):
- attachmentObject = yield self._newStoreHome.attachmentObjectWithName(name)
+ attachmentObject = yield self._newStoreHome.attachmentObjectWithID(name)
result = CalendarAttachment(
None,
attachmentObject,
name,
+ True,
principalCollections=self.principalCollections()
)
self.propagateTransaction(result)
@@ -1666,10 +1668,11 @@
class CalendarAttachment(_NewStoreFileMetaDataHelper, _GetChildHelper):
- def __init__(self, calendarObject, attachment, attachmentName, **kw):
+ def __init__(self, calendarObject, attachment, attachmentName, managed, **kw):
super(CalendarAttachment, self).__init__(**kw)
self._newStoreCalendarObject = calendarObject # This can be None for a managed attachment
self._newStoreAttachment = self._newStoreObject = attachment
+ self._managed = managed
self._dead_properties = NonePropertyStore(self)
self.attachmentName = attachmentName
@@ -1693,7 +1696,7 @@
# FIXME: CDT test to make sure that permissions are enforced.
# Cannot PUT to a managed attachment
- if self._newStoreAttachment.isManaged():
+ if self._managed:
raise HTTPError(FORBIDDEN)
content_type = request.headers.getHeader("content-type")
@@ -1752,7 +1755,7 @@
@inlineCallbacks
def http_DELETE(self, request):
# Cannot DELETE a managed attachment
- if self._newStoreAttachment.isManaged():
+ if self._managed:
raise HTTPError(FORBIDDEN)
if not self.exists():
Modified: CalendarServer/branches/users/cdaboo/managed-attachments/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/managed-attachments/txdav/caldav/datastore/sql.py 2012-11-13 17:07:03 UTC (rev 10026)
+++ CalendarServer/branches/users/cdaboo/managed-attachments/txdav/caldav/datastore/sql.py 2012-11-13 21:40:20 UTC (rev 10027)
@@ -49,8 +49,7 @@
from txdav.caldav.datastore.util import validateCalendarComponent, \
dropboxIDFromCalendarObject
from txdav.caldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject, \
- IAttachment, AttachmentStoreFailed, AttachmentStoreValidManagedID, \
- AttachmentRemoveFailed
+ IAttachment, AttachmentStoreFailed, AttachmentStoreValidManagedID
from txdav.common.datastore.sql import CommonHome, CommonHomeChild, \
CommonObjectResource, ECALENDARTYPE
from txdav.common.datastore.sql_legacy import PostgresLegacyIndexEmulator, \
@@ -285,8 +284,8 @@
@inlineCallbacks
- def attachmentObjectWithName(self, name):
- attach = (yield Attachment.load(self._txn, dropboxID=name))
+ def attachmentObjectWithID(self, managedID):
+ attach = (yield ManagedAttachment.load(self._txn, managedID))
returnValue(attach)
@@ -1179,7 +1178,7 @@
@inlineCallbacks
def remove(self):
# Need to also remove attachments
- yield Attachment.removeAllReferencesTo(self._txn, self._resourceID)
+ yield ManagedAttachment.resourceRemoved(self._txn, self._resourceID)
yield super(CalendarObject, self).remove()
@@ -1330,9 +1329,9 @@
# Now try and adjust the actual calendar data
calendar = (yield self.component())
- location = pathpattern % (self._parentCollection.ownerHome().name(), attachment.dropboxID(),)
+ location = pathpattern % (self._parentCollection.ownerHome().name(), attachment.managedID(),)
attach = Property("ATTACH", location, params={
- "MANAGED-ID": attachment.dropboxID(),
+ "MANAGED-ID": attachment.managedID(),
"MTAG": attachment.md5(),
"FMTTYPE": "%s/%s" % (attachment.contentType().mediaType, attachment.contentType().mediaSubtype),
"FILENAME": attachment.dispositionName(),
@@ -1340,6 +1339,9 @@
}, valuetype=PyCalendarValue.VALUETYPE_URI)
if rids is None:
calendar.addPropertyToAllComponents(attach)
+ else:
+ # TODO - per-recurrence attachments
+ pass
# Store the data
yield self.setComponent(calendar)
@@ -1351,7 +1353,7 @@
def updateAttachment(self, pathpattern, managed_id, content_type, filename, stream):
# First check the supplied managed-id is associated with this resource
- cobjs = (yield Attachment.referencesTo(self._txn, managed_id))
+ cobjs = (yield ManagedAttachment.referencesTo(self._txn, managed_id))
if self._resourceID not in cobjs:
raise AttachmentStoreValidManagedID
@@ -1360,10 +1362,15 @@
# We need to know the resource_ID of the home collection of the owner
# (not sharee) of this event
try:
- attachment = (yield self.attachmentWithManagedID(managed_id))
- if attachment is None:
+ # Check that this is a proper update
+ oldattachment = (yield self.attachmentWithManagedID(managed_id))
+ if oldattachment is None:
self.log_error("Missing managed attachment even though ATTACHMENT_CALENDAR_OBJECT indicates it is present: %s" % (managed_id,))
raise AttachmentStoreFailed
+
+ # We actually create a brand new attachment object for the update, but with the same managed-id. That way, other resources
+ # referencing the old attachment data will still see that.
+ attachment = (yield self.createManagedAttachment(managed_id))
t = attachment.store(content_type, filename)
yield readStream(stream, t.write)
except Exception, e:
@@ -1374,9 +1381,9 @@
# Now try and adjust the actual calendar data
calendar = (yield self.component())
- location = pathpattern % (self._parentCollection.ownerHome().name(), attachment.dropboxID(),)
+ location = pathpattern % (self._parentCollection.ownerHome().name(), attachment.managedID(),)
attach = Property("ATTACH", location, params={
- "MANAGED-ID": attachment.dropboxID(),
+ "MANAGED-ID": attachment.managedID(),
"MTAG": attachment.md5(),
"FMTTYPE": "%s/%s" % (attachment.contentType().mediaType, attachment.contentType().mediaSubtype),
"FILENAME": attachment.dispositionName(),
@@ -1394,42 +1401,49 @@
def removeAttachment(self, rids, managed_id):
# First check the supplied managed-id is associated with this resource
- cobjs = (yield Attachment.referencesTo(self._txn, managed_id))
+ cobjs = (yield ManagedAttachment.referencesTo(self._txn, managed_id))
if self._resourceID not in cobjs:
raise AttachmentStoreValidManagedID
# Now try and adjust the actual calendar data
+ all_removed = False
calendar = (yield self.component())
- calendar.removeAllPropertiesWithParameterMatch("ATTACH", "MANAGED-ID", managed_id)
+ if rids is None:
+ calendar.removeAllPropertiesWithParameterMatch("ATTACH", "MANAGED-ID", managed_id)
+ all_removed = True
+ else:
+ # TODO: per-recurrence removal
+ pass
# Store the data
yield self.setComponent(calendar)
# Remove it - this will take care of actually removing it from the store if there are
# no more references to the attachment
- yield self.removeManagedAttachmentWithName(managed_id)
+ if all_removed:
+ yield self.removeManagedAttachmentWithID(managed_id)
@inlineCallbacks
- def createManagedAttachment(self):
+ def createManagedAttachment(self, managedID=None):
# We need to know the resource_ID of the home collection of the owner
# (not sharee) of this event
sharerHomeID = (yield self._parentCollection.sharerHomeID())
- managedID = str(uuid.uuid4())
+ managedID = str(uuid.uuid4()) if managedID is None else managedID
returnValue((
- yield Attachment.create(
- self._txn, _ATTACHMENT_STATUS_MANAGED, managedID, "data", sharerHomeID, self._resourceID,
+ yield ManagedAttachment.create(
+ self._txn, managedID, sharerHomeID, self._resourceID,
)
))
def attachmentWithManagedID(self, managed_id):
- return Attachment.load(self._txn, dropboxID=managed_id)
+ return ManagedAttachment.load(self._txn, managed_id)
@inlineCallbacks
- def removeManagedAttachmentWithName(self, managed_id):
+ def removeManagedAttachmentWithID(self, managed_id):
attachment = (yield self.attachmentWithManagedID(managed_id))
yield attachment.removeFromResource(self._resourceID)
@@ -1442,8 +1456,8 @@
sharerHomeID = (yield self._parentCollection.sharerHomeID())
dropboxID = (yield self.dropboxID())
returnValue((
- yield Attachment.create(
- self._txn, _ATTACHMENT_STATUS_DROPBOX, dropboxID, name, sharerHomeID, self._resourceID,
+ yield DropBoxAttachment.create(
+ self._txn, dropboxID, name, sharerHomeID,
)
))
@@ -1455,7 +1469,7 @@
def attachmentWithName(self, name):
- return Attachment.load(self._txn, dropboxID=self._dropboxID, name=name)
+ return DropBoxAttachment.load(self._txn, self._dropboxID, name)
def attendeesCanManageAttachments(self):
@@ -1638,110 +1652,7 @@
return attachmentRoot.child(hasheduid[0:2]).child(hasheduid[2:4]).child(hasheduid)
- @classmethod
@inlineCallbacks
- def create(cls, txn, status, dropboxID, name, ownerHomeID, referencedBy):
- """
- Create a new Attachment object.
-
- @param txn: The transaction to use
- @type txn: L{CommonStoreTransaction}
- @param status: the type of attachment (dropbox or managed)
- @type status: C{int}
- @param dropboxID: the identifier for the attachment (dropbox id or managed id)
- @type dropboxID: C{str}
- @param name: the name of the attachment
- @type name: 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 (managed only)
- @type referencedBy: C{int}
- """
-
- # File system paths need to exist
- try:
- cls._attachmentPathRoot(txn, status, dropboxID).makedirs()
- except:
- pass
-
- # Now create the DB entry
- att = schema.ATTACHMENT
- rows = (yield Insert({
- att.CALENDAR_HOME_RESOURCE_ID : ownerHomeID,
- att.STATUS : status,
- att.DROPBOX_ID : dropboxID,
- att.CONTENT_TYPE : "",
- att.SIZE : 0,
- att.MD5 : "",
- att.PATH : name,
- att.DISPLAYNAME : None,
- }, Return=(att.ATTACHMENT_ID, att.CREATED, att.MODIFIED)).on(txn))
-
- row_iter = iter(rows[0])
- a_id = row_iter.next()
- created = sqltime(row_iter.next())
- modified = sqltime(row_iter.next())
-
- # Create the attachment<->calendar object relationship for managed attachments
- if status == _ATTACHMENT_STATUS_MANAGED:
- attco = schema.ATTACHMENT_CALENDAR_OBJECT
- rows = (yield Insert({
- attco.ATTACHMENT_ID : a_id,
- attco.CALENDAR_OBJECT_RESOURCE_ID : referencedBy,
- }).on(txn))
-
- attachment = cls(txn, a_id, status, dropboxID, name, ownerHomeID, True)
- attachment._created = created
- attachment._modified = modified
- returnValue(attachment)
-
-
- @classmethod
- @inlineCallbacks
- def load(cls, txn, dropboxID=None, name="data", attachmentID=None):
- attachment = cls(txn, attachmentID, None, dropboxID, name)
- attachment = (yield attachment.initFromStore())
- returnValue(attachment)
-
-
- @classmethod
- @inlineCallbacks
- def referencesTo(cls, txn, managedID):
- """
- Find all the calendar object resourceIds referenced by this supplied managed-id.
- """
- att = schema.ATTACHMENT
- attco = schema.ATTACHMENT_CALENDAR_OBJECT
- rows = (yield Select(
- [attco.CALENDAR_OBJECT_RESOURCE_ID, ],
- From=att.join(attco, att.ATTACHMENT_ID == attco.ATTACHMENT_ID, "inner"),
- Where=(att.DROPBOX_ID == managedID),
- ).on(txn))
- cobjs = set([row[0] for row in rows]) if rows is not None else set()
- returnValue(cobjs)
-
-
- @classmethod
- @inlineCallbacks
- def removeAllReferencesTo(cls, txn, resourceID):
- """
- Remove all attachments referencing the specified resource.
- """
-
- # Find all reference attachment-ids and dereference
- attco = schema.ATTACHMENT_CALENDAR_OBJECT
- rows = (yield Select(
- [attco.ATTACHMENT_ID, ],
- From=attco,
- Where=(attco.CALENDAR_OBJECT_RESOURCE_ID == resourceID),
- ).on(txn))
- aids = set([row[0] for row in rows]) if rows is not None else set()
- for aid in aids:
- attachment = (yield Attachment.load(txn, attachmentID=aid))
- (yield attachment.removeFromResource(resourceID))
-
-
- @inlineCallbacks
def initFromStore(self):
"""
Execute necessary SQL queries to retrieve attributes.
@@ -1855,31 +1766,6 @@
break
- @inlineCallbacks
- def removeFromResource(self, resourceID):
-
- # This must only be called for a managed attachment
- if self._attachmentStatus != _ATTACHMENT_STATUS_MANAGED:
- raise AttachmentRemoveFailed
-
- # Delete the reference
- attco = schema.ATTACHMENT_CALENDAR_OBJECT
- yield Delete(
- From=attco,
- Where=(attco.ATTACHMENT_ID == self._attachmentID).And(
- attco.CALENDAR_OBJECT_RESOURCE_ID == resourceID),
- ).on(self._txn)
-
- # References still exist - if not remove actual attachment
- rows = (yield Select(
- [attco.CALENDAR_OBJECT_RESOURCE_ID, ],
- From=attco,
- Where=(attco.ATTACHMENT_ID == self._attachmentID),
- ).on(self._txn))
- if len(rows) == 0:
- yield self.remove()
-
-
def _internalRemove(self):
"""
Just delete the row; don't do any accounting / bookkeeping. (This is
@@ -1916,4 +1802,203 @@
return self._dispositionName
+
+class DropBoxAttachment(Attachment):
+
+ @classmethod
+ @inlineCallbacks
+ def create(cls, txn, dropboxID, name, ownerHomeID):
+ """
+ Create a new Attachment object.
+
+ @param txn: The transaction to use
+ @type txn: L{CommonStoreTransaction}
+ @param dropboxID: the identifier for the attachment (dropbox id or managed id)
+ @type dropboxID: C{str}
+ @param name: the name of the attachment
+ @type name: C{str}
+ @param ownerHomeID: the resource-id of the home collection of the attachment owner
+ @type ownerHomeID: C{int}
+ """
+
+ # File system paths need to exist
+ try:
+ cls._attachmentPathRoot(txn, _ATTACHMENT_STATUS_DROPBOX, dropboxID).makedirs()
+ except:
+ pass
+
+ # Now create the DB entry
+ att = schema.ATTACHMENT
+ rows = (yield Insert({
+ att.CALENDAR_HOME_RESOURCE_ID : ownerHomeID,
+ att.STATUS : _ATTACHMENT_STATUS_DROPBOX,
+ att.DROPBOX_ID : dropboxID,
+ att.CONTENT_TYPE : "",
+ att.SIZE : 0,
+ att.MD5 : "",
+ att.PATH : name,
+ att.DISPLAYNAME : None,
+ }, Return=(att.ATTACHMENT_ID, att.CREATED, att.MODIFIED)).on(txn))
+
+ row_iter = iter(rows[0])
+ a_id = row_iter.next()
+ created = sqltime(row_iter.next())
+ modified = sqltime(row_iter.next())
+
+ attachment = cls(txn, a_id, _ATTACHMENT_STATUS_DROPBOX, dropboxID, name, ownerHomeID, True)
+ attachment._created = created
+ attachment._modified = modified
+ returnValue(attachment)
+
+
+ @classmethod
+ @inlineCallbacks
+ def load(cls, txn, dropboxID, name):
+ attachment = cls(txn, None, _ATTACHMENT_STATUS_DROPBOX, dropboxID, name)
+ attachment = (yield attachment.initFromStore())
+ returnValue(attachment)
+
+
+
+class ManagedAttachment(Attachment):
+
+ _PATH_NAME = "data"
+
+ @classmethod
+ @inlineCallbacks
+ def create(cls, txn, managedID, ownerHomeID, referencedBy):
+ """
+ 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 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
+ @type referencedBy: C{int}
+ """
+
+ # File system paths need to exist
+ try:
+ cls._attachmentPathRoot(txn, _ATTACHMENT_STATUS_MANAGED, managedID).makedirs()
+ except:
+ pass
+
+ # Now create the DB entry
+ att = schema.ATTACHMENT
+ rows = (yield Insert({
+ att.CALENDAR_HOME_RESOURCE_ID : ownerHomeID,
+ att.STATUS : _ATTACHMENT_STATUS_MANAGED,
+ att.DROPBOX_ID : managedID,
+ att.CONTENT_TYPE : "",
+ att.SIZE : 0,
+ att.MD5 : "",
+ att.PATH : cls._PATH_NAME,
+ att.DISPLAYNAME : None,
+ }, Return=(att.ATTACHMENT_ID, att.CREATED, att.MODIFIED)).on(txn))
+
+ row_iter = iter(rows[0])
+ a_id = row_iter.next()
+ created = sqltime(row_iter.next())
+ modified = sqltime(row_iter.next())
+
+ # Create the attachment<->calendar object relationship for managed attachments
+ attco = schema.ATTACHMENT_CALENDAR_OBJECT
+ rows = (yield Insert({
+ attco.ATTACHMENT_ID : a_id,
+ attco.MANAGED_ID : managedID,
+ attco.CALENDAR_OBJECT_RESOURCE_ID : referencedBy,
+ }).on(txn))
+
+ attachment = cls(txn, a_id, _ATTACHMENT_STATUS_MANAGED, managedID, cls._PATH_NAME, ownerHomeID, True)
+ attachment._managedID = managedID
+ attachment._created = created
+ attachment._modified = modified
+ returnValue(attachment)
+
+
+ @classmethod
+ @inlineCallbacks
+ def load(cls, txn, managedID=None):
+ attco = schema.ATTACHMENT_CALENDAR_OBJECT
+ rows = (yield Select(
+ [attco.ATTACHMENT_ID, ],
+ From=attco,
+ Where=(attco.MANAGED_ID == managedID),
+ ).on(txn))
+ aids = [row[0] for row in rows] if rows is not None else ()
+ if len(aids) == 0:
+ returnValue(None)
+ elif len(aids) != 1:
+ raise AttachmentStoreValidManagedID
+
+ attachment = cls(txn, aids[0], _ATTACHMENT_STATUS_MANAGED, None, cls._PATH_NAME)
+ attachment = (yield attachment.initFromStore())
+ attachment._managedID = managedID
+ returnValue(attachment)
+
+
+ @classmethod
+ @inlineCallbacks
+ def referencesTo(cls, txn, managedID):
+ """
+ Find all the calendar object resourceIds referenced by this supplied managed-id.
+ """
+ attco = schema.ATTACHMENT_CALENDAR_OBJECT
+ rows = (yield Select(
+ [attco.CALENDAR_OBJECT_RESOURCE_ID, ],
+ From=attco,
+ Where=(attco.MANAGED_ID == managedID),
+ ).on(txn))
+ cobjs = set([row[0] for row in rows]) if rows is not None else set()
+ returnValue(cobjs)
+
+
+ @classmethod
+ @inlineCallbacks
+ def resourceRemoved(cls, txn, resourceID):
+ """
+ Remove all attachments referencing the specified resource.
+ """
+
+ # Find all reference attachment-ids and dereference
+ attco = schema.ATTACHMENT_CALENDAR_OBJECT
+ rows = (yield Select(
+ [attco.MANAGED_ID, ],
+ From=attco,
+ Where=(attco.CALENDAR_OBJECT_RESOURCE_ID == resourceID),
+ ).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))
+ (yield attachment.removeFromResource(resourceID))
+
+
+ def managedID(self):
+ return self._managedID
+
+
+ @inlineCallbacks
+ def removeFromResource(self, resourceID):
+
+ # Delete the reference
+ attco = schema.ATTACHMENT_CALENDAR_OBJECT
+ yield Delete(
+ From=attco,
+ Where=(attco.ATTACHMENT_ID == self._attachmentID).And(
+ attco.CALENDAR_OBJECT_RESOURCE_ID == resourceID),
+ ).on(self._txn)
+
+ # References still exist - if not remove actual attachment
+ rows = (yield Select(
+ [attco.CALENDAR_OBJECT_RESOURCE_ID, ],
+ From=attco,
+ Where=(attco.ATTACHMENT_ID == self._attachmentID),
+ ).on(self._txn))
+ if len(rows) == 0:
+ yield self.remove()
+
+
Calendar._objectResourceClass = CalendarObject
Modified: CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/current.sql 2012-11-13 17:07:03 UTC (rev 10026)
+++ CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/current.sql 2012-11-13 21:40:20 UTC (rev 10027)
@@ -329,10 +329,12 @@
-- Many-to-many relationship between attachments and calendar objects
create table ATTACHMENT_CALENDAR_OBJECT (
- ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
- CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ 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
+ primary key(ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique(MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
);
-- Enumeration of attachment status
Modified: CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_12_to_13.sql
===================================================================
--- CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_12_to_13.sql 2012-11-13 17:07:03 UTC (rev 10026)
+++ CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_12_to_13.sql 2012-11-13 21:40:20 UTC (rev 10027)
@@ -32,8 +32,9 @@
add unique(DROPBOX_ID, PATH);
create table ATTACHMENT_CALENDAR_OBJECT (
- ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
- CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ 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
);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121113/9f6a2160/attachment-0001.html>
More information about the calendarserver-changes
mailing list