[CalendarServer-changes] [10162] CalendarServer/branches/users/gaya/sharedgroups
source_changes at macosforge.org
source_changes at macosforge.org
Wed Dec 12 14:11:41 PST 2012
Revision: 10162
http://trac.calendarserver.org//changeset/10162
Author: gaya at apple.com
Date: 2012-12-12 14:11:41 -0800 (Wed, 12 Dec 2012)
Log Message:
-----------
merge from trunk
Modified Paths:
--------------
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/accesslog.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_purge_old_events.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/webcal/resource.py
CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-test.plist
CalendarServer/branches/users/gaya/sharedgroups/contrib/tools/readStats.py
CalendarServer/branches/users/gaya/sharedgroups/support/build.sh
CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/dal/syntax.py
CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/file.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/test/test_sql.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/file.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current.sql
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_tables.py
Added Paths:
-----------
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/logAnalysis.py
CalendarServer/branches/users/gaya/sharedgroups/calendarserver/test/test_methodDescriptor.py
CalendarServer/branches/users/gaya/sharedgroups/twext/python/test/test_timezone.py
CalendarServer/branches/users/gaya/sharedgroups/twext/python/timezone.py
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/oracle-dialect/v13.sql
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/postgres-dialect/v13.sql
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql
CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql
Property Changed:
----------------
CalendarServer/branches/users/gaya/sharedgroups/
Property changes on: CalendarServer/branches/users/gaya/sharedgroups
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:9885-10150
+ /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:9885-10159
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/accesslog.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/accesslog.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/accesslog.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -35,30 +35,37 @@
psutil = None
import time
-from twisted.internet import protocol, task
-from twisted.protocols import amp
+from calendarserver.logAnalysis import getAdjustedMethodName, \
+ getAdjustedClientName
+
+from twext.python.log import Logger
from twext.web2 import iweb
-from txdav.xml import element as davxml
from twext.web2.log import BaseCommonAccessLoggingObserver
from twext.web2.log import LogWrapperResource
-from twext.python.log import Logger
+from twisted.internet import protocol, task
+from twisted.protocols import amp
from twistedcaldav.config import config
from twistedcaldav.directory.directory import DirectoryService
+from txdav.xml import element as davxml
+
log = Logger()
class DirectoryLogWrapperResource(LogWrapperResource):
-
+
def __init__(self, resource, directory):
super(DirectoryLogWrapperResource, self).__init__(resource)
-
+
self.directory = directory
-
+
+
def getDirectory(self):
return self.directory
+
+
class CommonAccessLoggingObserverExtensions(BaseCommonAccessLoggingObserver):
"""
A base class for our extension to the L{BaseCommonAccessLoggingObserver}
@@ -69,7 +76,7 @@
format = None
formatArgs = None
if eventDict.get("interface") is iweb.IRequest:
-
+
request = eventDict["request"]
response = eventDict["response"]
loginfo = eventDict["loginfo"]
@@ -82,7 +89,7 @@
uidz = None
if hasattr(request, "authzUser") and str(request.authzUser.children[0]) != uidn:
uidz = str(request.authzUser.children[0])
-
+
def convertUIDtoShortName(uid):
uid = uid.rstrip("/")
uid = uid[uid.rfind("/") + 1:]
@@ -94,11 +101,11 @@
return "(%s)%s" % (record.recordType, record.shortNames[0],)
else:
return uid
-
+
uidn = convertUIDtoShortName(uidn)
if uidz:
uidz = convertUIDtoShortName(uidz)
-
+
if uidn and uidz:
uid = '"%s as %s"' % (uidn, uidz,)
else:
@@ -142,10 +149,10 @@
if config.EnableExtendedAccessLog:
format += ' i=%(serverInstance)s'
formatArgs["serverInstance"] = config.LogID if config.LogID else "0"
-
+
format += ' or=%(outstandingRequests)s'
formatArgs["outstandingRequests"] = request.chanRequest.channel.factory.outstandingRequests
-
+
# Tags for time stamps collected along the way - the first one in the list is the initial
# time for request creation - we use that to track the entire request/response time
nowtime = time.time()
@@ -213,40 +220,42 @@
if config.EnableExtendedAccessLog:
format += ' p=%(serverPort)s'
formatArgs["serverPort"] = overloaded.transport.server.port
-
+
format += ' or=%(outstandingRequests)s'
formatArgs["outstandingRequests"] = overloaded.outstandingRequests
-
# Write anything we got to the log and stats
if format is not None:
# sanitize output to mitigate log injection
- for k,v in formatArgs.items():
+ for k, v in formatArgs.items():
if not isinstance(v, basestring):
continue
v = v.replace("\r", "\\r")
v = v.replace("\n", "\\n")
v = v.replace("\"", "\\\"")
formatArgs[k] = v
-
+
formatArgs["type"] = "access-log"
formatArgs["log-format"] = format
self.logStats(formatArgs)
+
+
class RotatingFileAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
"""
Class to do "apache" style access logging to a rotating log file. The log
file is rotated after midnight each day.
-
+
This class also currently handles the collection of system and log statistics.
"""
def __init__(self, logpath):
- self.logpath = logpath
+ self.logpath = logpath
self.systemStats = None
self.statsByMinute = []
+
def accessLog(self, message, allowrotate=True):
"""
Log a message to the file and possibly rotate if date has changed.
@@ -261,6 +270,7 @@
self.rotate()
self.f.write(message + "\n")
+
def start(self):
"""
Start logging. Open the log file and log an "open" message.
@@ -270,6 +280,7 @@
self._open()
self.accessLog("Log opened - server start: [%s]." % (datetime.datetime.now().ctime(),))
+
def stop(self):
"""
Stop logging. Close the log file and log an "open" message.
@@ -278,10 +289,11 @@
self.accessLog("Log closed - server stop: [%s]." % (datetime.datetime.now().ctime(),), False)
super(RotatingFileAccessLoggingObserver, self).stop()
self._close()
-
+
if self.systemStats is not None:
self.systemStats.stop()
+
def _open(self):
"""
Open the log file.
@@ -290,6 +302,7 @@
self.f = open(self.logpath, "a", 1)
self.lastDate = self.toDate(os.stat(self.logpath)[8])
+
def _close(self):
"""
Close the log file.
@@ -297,6 +310,7 @@
self.f.close()
+
def flush(self):
"""
Flush the log file.
@@ -304,6 +318,7 @@
self.f.flush()
+
def shouldRotate(self):
"""
Rotate when the date has changed since last write
@@ -314,6 +329,7 @@
else:
return False
+
def toDate(self, *args):
"""
Convert a unixtime to (year, month, day) localtime tuple,
@@ -326,6 +342,7 @@
# primarily so this can be unit tested easily
return time.localtime(*args)[:3]
+
def suffix(self, tupledate):
"""
Return the suffix given a (year, month, day) tuple or unixtime
@@ -337,6 +354,7 @@
# try taking a float unixtime
return "_".join(map(str, self.toDate(tupledate)))
+
def rotate(self):
"""
Rotate the file and create a new one.
@@ -356,10 +374,11 @@
self._open()
self.accessLog("Log opened - rotated: [%s]." % (datetime.datetime.now().ctime(),), False)
- def logStats(self, stats):
- """
+
+ def logStats(self, stats):
+ """
Update stats
- """
+ """
if self.systemStats is None:
self.systemStats = SystemMonitor()
@@ -367,18 +386,19 @@
# Currently only storing stats for access log type
if "type" not in stats or stats["type"] != "access-log":
return
-
+
currentStats = self.ensureSequentialStats()
self.updateStats(currentStats, stats)
-
+
if stats["type"] == "access-log":
self.accessLog(stats["log-format"] % stats)
- def getStats(self):
- """
- Return the stats
+
+ def getStats(self):
"""
-
+ Return the stats
+ """
+
if self.systemStats is None:
self.systemStats = SystemMonitor()
@@ -407,17 +427,18 @@
self.mergeStats(oneHour, stat)
printStats = {
- "System":self.systemStats.items,
- "Current":currentStats,
- "1 Minute":previousMinute,
- "5 Minutes":fiveMinutes,
- "1 Hour":oneHour,
+ "System": self.systemStats.items,
+ "Current": currentStats,
+ "1 Minute": previousMinute,
+ "5 Minutes": fiveMinutes,
+ "1 Hour": oneHour,
}
return json.dumps(printStats)
+
def ensureSequentialStats(self):
"""
- Make sure the list of timed stats is contiguous wrt time.
+ Make sure the list of timed stats is contiguous wrt time.
"""
dtindex = int(time.time() / 60.0) * 60
@@ -431,8 +452,9 @@
self.statsByMinute.append((dtindex, self.initStats(),))
return self.statsByMinute[-1][1]
+
def initStats(self):
-
+
def initTimeHistogram():
return {
"<10ms": 0,
@@ -447,33 +469,41 @@
}
return {
- "requests" : 0,
- "method" : collections.defaultdict(int),
- "uid" : collections.defaultdict(int),
- "500" : 0,
- "t" : 0.0,
- "t-resp-wr": 0.0,
- "slots" : 0,
- "T" : initTimeHistogram(),
- "T-RESP-WR": initTimeHistogram(),
- "T-MAX" : 0.0,
- "cpu" : self.systemStats.items["cpu use"],
+ "requests" : 0,
+ "method" : collections.defaultdict(int),
+ "method-t" : collections.defaultdict(float),
+ "uid" : collections.defaultdict(int),
+ "user-agent" : collections.defaultdict(int),
+ "500" : 0,
+ "t" : 0.0,
+ "t-resp-wr" : 0.0,
+ "slots" : 0,
+ "T" : initTimeHistogram(),
+ "T-RESP-WR" : initTimeHistogram(),
+ "T-MAX" : 0.0,
+ "cpu" : self.systemStats.items["cpu use"],
}
+
def updateStats(self, current, stats):
# Gather specific information and aggregate into our persistent stats
+ adjustedMethod = getAdjustedMethodName(stats)
+ adjustedClient = getAdjustedClientName(stats)
+
if current["requests"] == 0:
current["cpu"] = 0.0
current["requests"] += 1
- current["method"][stats["method"]] += 1
+ current["method"][adjustedMethod] += 1
+ current["method-t"][adjustedMethod] += stats.get("t", 0.0)
current["uid"][stats["uid"]] += 1
+ current["user-agent"][adjustedClient] += 1
if stats["statusCode"] >= 500:
current["500"] += 1
current["t"] += stats.get("t", 0.0)
current["t-resp-wr"] += stats.get("t-resp-wr", 0.0)
current["slots"] += stats.get("outstandingRequests", 0)
current["cpu"] += self.systemStats.items["cpu use"]
-
+
def histogramUpdate(t, key):
if t >= 60000.0:
current[key][">60s"] += 1
@@ -493,7 +523,7 @@
current[key]["Over 1s"] += 1
elif t >= 10000.0:
current[key]["Over 10s"] += 1
-
+
t = stats.get("t", None)
if t is not None:
histogramUpdate(t, "T")
@@ -502,6 +532,7 @@
if t is not None:
histogramUpdate(t, "T-RESP-WR")
+
def mergeStats(self, current, stats):
# Gather specific information and aggregate into our persistent stats
if current["requests"] == 0:
@@ -509,14 +540,18 @@
current["requests"] += stats["requests"]
for method in stats["method"].keys():
current["method"][method] += stats["method"][method]
+ for method in stats["method-t"].keys():
+ current["method-t"][method] += stats["method-t"][method]
for uid in stats["uid"].keys():
current["uid"][uid] += stats["uid"][uid]
+ for ua in stats["user-agent"].keys():
+ current["user-agent"][ua] += stats["user-agent"][ua]
current["500"] += stats["500"]
current["t"] += stats["t"]
current["t-resp-wr"] += stats["t-resp-wr"]
current["slots"] += stats["slots"]
current["cpu"] += stats["cpu"]
-
+
def histogramUpdate(t, key):
if t >= 60000.0:
current[key][">60s"] += 1
@@ -536,7 +571,7 @@
current[key]["Over 1s"] += 1
elif t >= 10000.0:
current[key]["Over 10s"] += 1
-
+
for bin in stats["T"].keys():
current["T"][bin] += stats["T"][bin]
current["T-MAX"] = max(current["T-MAX"], stats["T-MAX"])
@@ -547,10 +582,10 @@
class SystemMonitor(object):
"""
- Keeps track of system usage information. This installs a reacxtor task to
+ Keeps track of system usage information. This installs a reactor task to
run about once per second and track system use.
"""
-
+
CPUStats = collections.namedtuple("CPUStats", ("total", "idle",))
def __init__(self):
@@ -561,24 +596,26 @@
"memory percent": 0.0,
"start time" : time.time(),
}
-
+
if psutil is not None:
times = psutil.cpu_times()
self.previous_cpu = SystemMonitor.CPUStats(sum(times), times.idle,)
else:
self.previous_cpu = SystemMonitor.CPUStats(0, 0)
-
+
self.task = task.LoopingCall(self.update)
self.task.start(1.0)
-
+
+
def stop(self):
"""
Just stop the task
"""
self.task.stop()
+
def update(self):
-
+
# CPU usage based on diff'ing CPU times
if psutil is not None:
times = psutil.cpu_times()
@@ -588,18 +625,20 @@
except ZeroDivisionError:
self.items["cpu use"] = 0.0
self.previous_cpu = cpu_now
-
+
# Memory usage
if psutil is not None:
mem = psutil.virtual_memory()
self.items["memory used"] = mem.used
self.items["memory percent"] = mem.percent
-
+
+
class LogStats(amp.Command):
arguments = [("message", amp.String())]
+
class AMPCommonAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
def __init__(self):
self.protocol = None
@@ -620,18 +659,18 @@
self.flushBuffer()
- def logStats(self, message):
- """
- Log server stats via the remote AMP Protocol
- """
+ def logStats(self, message):
+ """
+ Log server stats via the remote AMP Protocol
+ """
if self.protocol is not None:
- message=json.dumps(message)
+ message = json.dumps(message)
if isinstance(message, unicode):
message = message.encode("utf-8")
- d = self.protocol.callRemote(LogStats, message=message)
- d.addErrback(log.err)
- else:
+ d = self.protocol.callRemote(LogStats, message=message)
+ d.addErrback(log.err)
+ else:
self._buffer.append(message)
@@ -646,10 +685,11 @@
super(AMPLoggingProtocol, self).__init__()
- def logStats(self, message):
+
+ def logStats(self, message):
stats = json.loads(message)
- self.observer.logStats(stats)
- return {}
+ self.observer.logStats(stats)
+ return {}
LogStats.responder(logStats)
@@ -670,6 +710,3 @@
def buildProtocol(self, addr):
return AMPLoggingProtocol(self.observer)
-
-
-
Copied: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/logAnalysis.py (from rev 10159, CalendarServer/trunk/calendarserver/logAnalysis.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/logAnalysis.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/logAnalysis.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,373 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+# Adjust method names
+
+# PROPFINDs
+METHOD_PROPFIND_CALENDAR_HOME = "PROPFIND Calendar Home"
+METHOD_PROPFIND_CACHED_CALENDAR_HOME = "PROPFIND cached Calendar Home"
+METHOD_PROPFIND_CALENDAR = "PROPFIND Calendar"
+METHOD_PROPFIND_INBOX = "PROPFIND Inbox"
+METHOD_PROPFIND_ADDRESSBOOK_HOME = "PROPFIND Adbk Home"
+METHOD_PROPFIND_CACHED_ADDRESSBOOK_HOME = "PROPFIND cached Adbk Home"
+METHOD_PROPFIND_ADDRESSBOOK = "PROPFIND Adbk"
+METHOD_PROPFIND_DIRECTORY = "PROPFIND Directory"
+METHOD_PROPFIND_PRINCIPALS = "PROPFIND Principals"
+METHOD_PROPFIND_CACHED_PRINCIPALS = "PROPFIND cached Principals"
+
+# PROPPATCHs
+METHOD_PROPPATCH_CALENDAR = "PROPPATCH Calendar"
+METHOD_PROPPATCH_ADDRESSBOOK = "PROPPATCH Adbk Home"
+
+# REPORTs
+METHOD_REPORT_CALENDAR_MULTIGET = "REPORT cal-multi"
+METHOD_REPORT_CALENDAR_QUERY = "REPORT cal-query"
+METHOD_REPORT_CALENDAR_FREEBUSY = "REPORT freebusy"
+METHOD_REPORT_CALENDAR_SYNC = "REPORT cal-sync"
+METHOD_REPORT_ADDRESSBOOK_MULTIGET = "REPORT adbk-multi"
+METHOD_REPORT_ADDRESSBOOK_QUERY = "REPORT adbk-query"
+METHOD_REPORT_DIRECTORY_QUERY = "REPORT dir-query"
+METHOD_REPORT_ADDRESSBOOK_SYNC = "REPORT adbk-sync"
+METHOD_REPORT_P_SEARCH_P_SET = "REPORT p-set"
+METHOD_REPORT_P_P_SEARCH = "REPORT p-search"
+METHOD_REPORT_EXPAND_P = "REPORT expand"
+
+# POSTs
+METHOD_POST_CALENDAR_HOME = "POST Calendar Home"
+METHOD_POST_CALENDAR = "POST Calendar"
+METHOD_POST_CALENDAR_OBJECT = "POST Calendar Object"
+METHOD_POST_ADDRESSBOOK_HOME = "POST Adbk Home"
+METHOD_POST_ADDRESSBOOK = "POST Adbk"
+METHOD_POST_ISCHEDULE_FREEBUSY = "POST Freebusy iSchedule"
+METHOD_POST_ISCHEDULE = "POST iSchedule"
+METHOD_POST_TIMEZONES = "POST Timezones"
+METHOD_POST_FREEBUSY = "POST Freebusy"
+METHOD_POST_ORGANIZER = "POST Organizer"
+METHOD_POST_ATTENDEE = "POST Attendee"
+METHOD_POST_OUTBOX = "POST Outbox"
+METHOD_POST_APNS = "POST apns"
+
+# PUTs
+METHOD_PUT_ICS = "PUT ics"
+METHOD_PUT_ORGANIZER = "PUT Organizer"
+METHOD_PUT_ATTENDEE = "PUT Attendee"
+METHOD_PUT_DROPBOX = "PUT dropbox"
+METHOD_PUT_VCF = "PUT VCF"
+
+# GETs
+METHOD_GET_CALENDAR_HOME = "GET Calendar Home"
+METHOD_GET_CALENDAR = "GET Calendar"
+METHOD_GET_ICS = "GET ics"
+METHOD_GET_INBOX_ICS = "GET inbox ics"
+METHOD_GET_DROPBOX = "GET dropbox"
+METHOD_GET_ADDRESSBOOK_HOME = "GET Adbk Home"
+METHOD_GET_ADDRESSBOOK = "GET Adbk"
+METHOD_GET_VCF = "GET VCF"
+METHOD_GET_TIMEZONES = "GET Timezones"
+
+# DELETEs
+METHOD_DELETE_CALENDAR_HOME = "DELETE Calendar Home"
+METHOD_DELETE_CALENDAR = "DELETE Calendar"
+METHOD_DELETE_ICS = "DELETE ics"
+METHOD_DELETE_INBOX_ICS = "DELETE inbox ics"
+METHOD_DELETE_DROPBOX = "DELETE dropbox"
+METHOD_DELETE_ADDRESSBOOK_HOME = "DELETE Adbk Home"
+METHOD_DELETE_ADDRESSBOOK = "DELETE Adbk"
+METHOD_DELETE_VCF = "DELETE vcf"
+
+
+def getAdjustedMethodName(stats):
+
+ method = stats["method"]
+ uribits = stats["uri"].rstrip("/").split('/')[1:]
+ if len(uribits) == 0:
+ uribits = [stats["uri"]]
+
+ calendar_specials = ("attachments", "dropbox", "notification", "freebusy", "outbox",)
+ adbk_specials = ("notification",)
+
+ def _PROPFIND():
+ cached = "cached" in stats
+
+ if uribits[0] == "calendars":
+
+ if len(uribits) == 3:
+ return METHOD_PROPFIND_CACHED_CALENDAR_HOME if cached else METHOD_PROPFIND_CALENDAR_HOME
+ elif len(uribits) > 3:
+ if uribits[3] in calendar_specials:
+ return "PROPFIND %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ if uribits[3] == "inbox":
+ return METHOD_PROPFIND_INBOX
+ else:
+ return METHOD_PROPFIND_CALENDAR
+
+ elif uribits[0] == "addressbooks":
+
+ if len(uribits) == 3:
+ return METHOD_PROPFIND_CACHED_ADDRESSBOOK_HOME if cached else METHOD_PROPFIND_ADDRESSBOOK_HOME
+ elif len(uribits) > 3:
+ if uribits[3] in adbk_specials:
+ return "PROPFIND %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ return METHOD_PROPFIND_ADDRESSBOOK
+
+ elif uribits[0] == "directory":
+ return METHOD_PROPFIND_DIRECTORY
+
+ elif uribits[0] == "principals":
+ return METHOD_PROPFIND_CACHED_PRINCIPALS if cached else METHOD_PROPFIND_PRINCIPALS
+
+ return method
+
+
+ def _REPORT():
+
+ if "(" in method:
+ report_type = method.split("}" if "}" in method else ":")[1][:-1]
+ if report_type == "addressbook-query":
+ if uribits[0] == "directory":
+ report_type = "directory-query"
+ if report_type == "sync-collection":
+ if uribits[0] == "calendars":
+ report_type = "cal-sync"
+ elif uribits[0] == "addressbooks":
+ report_type = "adbk-sync"
+ mappedNames = {
+ "calendar-multiget" : METHOD_REPORT_CALENDAR_MULTIGET,
+ "calendar-query" : METHOD_REPORT_CALENDAR_QUERY,
+ "free-busy-query" : METHOD_REPORT_CALENDAR_FREEBUSY,
+ "cal-sync" : METHOD_REPORT_CALENDAR_SYNC,
+ "addressbook-multiget" : METHOD_REPORT_ADDRESSBOOK_MULTIGET,
+ "addressbook-query" : METHOD_REPORT_ADDRESSBOOK_QUERY,
+ "directory-query" : METHOD_REPORT_DIRECTORY_QUERY,
+ "adbk-sync" : METHOD_REPORT_ADDRESSBOOK_SYNC,
+ "principal-search-property-set" : METHOD_REPORT_P_SEARCH_P_SET,
+ "principal-property-search" : METHOD_REPORT_P_P_SEARCH,
+ "expand-property" : METHOD_REPORT_EXPAND_P,
+ }
+ return mappedNames.get(report_type, "REPORT %s" % (report_type,))
+
+ return method
+
+
+ def _PROPPATCH():
+
+ if uribits[0] == "calendars":
+ return METHOD_PROPPATCH_CALENDAR
+ elif uribits[0] == "addressbooks":
+ return METHOD_PROPPATCH_ADDRESSBOOK
+
+ return method
+
+
+ def _POST():
+
+ if uribits[0] == "calendars":
+
+ if len(uribits) == 3:
+ return METHOD_POST_CALENDAR_HOME
+ elif len(uribits) == 4:
+ if uribits[3] == "outbox":
+ if "recipients" in stats:
+ return METHOD_POST_FREEBUSY
+ elif "freebusy" in stats:
+ return METHOD_POST_FREEBUSY
+ elif "itip.request" in stats or "itip.cancel" in stats:
+ return METHOD_POST_ORGANIZER
+ elif "itip.reply" in stats:
+ return METHOD_POST_ATTENDEE
+ else:
+ return METHOD_POST_OUTBOX
+ elif uribits[3] in calendar_specials:
+ pass
+ else:
+ return METHOD_POST_CALENDAR
+ elif len(uribits) == 5:
+ return METHOD_POST_CALENDAR_OBJECT
+
+ elif uribits[0] == "addressbooks":
+
+ if len(uribits) == 3:
+ return METHOD_POST_ADDRESSBOOK_HOME
+ elif len(uribits) == 4:
+ if uribits[3] in adbk_specials:
+ pass
+ else:
+ return METHOD_POST_ADDRESSBOOK
+
+ elif uribits[0] == "ischedule":
+ if "fb-cached" in stats or "fb-uncached" in stats or "freebusy" in stats:
+ return METHOD_POST_ISCHEDULE_FREEBUSY
+ else:
+ return METHOD_POST_ISCHEDULE
+
+ elif uribits[0].startswith("timezones"):
+ return METHOD_POST_TIMEZONES
+
+ elif uribits[0].startswith("apns"):
+ return METHOD_POST_APNS
+
+ return method
+
+
+ def _PUT():
+
+ if uribits[0] == "calendars":
+ if len(uribits) > 3:
+ if uribits[3] in calendar_specials:
+ return "PUT %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ pass
+ else:
+ if "itip.requests" in stats:
+ return METHOD_PUT_ORGANIZER
+ elif "itip.reply" in stats:
+ return METHOD_PUT_ATTENDEE
+ else:
+ return METHOD_PUT_ICS
+
+ elif uribits[0] == "addressbooks":
+ if len(uribits) > 3:
+ if uribits[3] in adbk_specials:
+ return "PUT %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ pass
+ else:
+ return METHOD_PUT_VCF
+
+ return method
+
+
+ def _GET():
+
+ if uribits[0] == "calendars":
+
+ if len(uribits) == 3:
+ return METHOD_GET_CALENDAR_HOME
+ elif len(uribits) > 3:
+ if uribits[3] in calendar_specials:
+ return "GET %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ return METHOD_GET_CALENDAR
+ elif uribits[3] == "inbox":
+ return METHOD_GET_INBOX_ICS
+ else:
+ return METHOD_GET_ICS
+
+ elif uribits[0] == "addressbooks":
+
+ if len(uribits) == 3:
+ return METHOD_GET_ADDRESSBOOK_HOME
+ elif len(uribits) > 3:
+ if uribits[3] in adbk_specials:
+ return "GET %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ return METHOD_GET_ADDRESSBOOK
+ else:
+ return METHOD_GET_VCF
+
+ elif uribits[0].startswith("timezones"):
+ return METHOD_GET_TIMEZONES
+
+ return method
+
+
+ def _DELETE():
+
+ if uribits[0] == "calendars":
+
+ if len(uribits) == 3:
+ return METHOD_DELETE_CALENDAR_HOME
+ elif len(uribits) > 3:
+ if uribits[3] in calendar_specials:
+ return "DELETE %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ return METHOD_DELETE_CALENDAR
+ elif uribits[3] == "inbox":
+ return METHOD_DELETE_INBOX_ICS
+ else:
+ return METHOD_DELETE_ICS
+
+ elif uribits[0] == "addressbooks":
+
+ if len(uribits) == 3:
+ return METHOD_DELETE_ADDRESSBOOK_HOME
+ elif len(uribits) > 3:
+ if uribits[3] in adbk_specials:
+ return "DELETE %s" % (uribits[3],)
+ elif len(uribits) == 4:
+ return METHOD_DELETE_ADDRESSBOOK
+ else:
+ return METHOD_DELETE_VCF
+
+ return method
+
+
+ def _ANY():
+ return method
+
+ return {
+ "DELETE" : _DELETE,
+ "GET" : _GET,
+ "POST" : _POST,
+ "PROPFIND" : _PROPFIND,
+ "PROPPATCH" : _PROPPATCH,
+ "PUT" : _PUT,
+ "REPORT" : _REPORT,
+ }.get(method.split("(")[0], _ANY)()
+
+
+
+versionClients = (
+ "Mac OS X/",
+ "iOS/",
+ "iCal/",
+ "iPhone/",
+ "CalendarAgent",
+ "Calendar/",
+ "CoreDAV/",
+ "Safari/",
+ "dataaccessd",
+ "curl/",
+ "DAVKit",
+)
+
+quickclients = (
+ ("InterMapper/", "InterMapper"),
+ ("CardDAVPlugin/", "CardDAVPlugin"),
+ ("Address%20Book/", "AddressBook"),
+ ("AddressBook/", "AddressBook"),
+ ("Mail/", "Mail"),
+ ("iChat/", "iChat"),
+)
+
+def getAdjustedClientName(stats):
+
+ userAgent = stats["userAgent"]
+ for client in versionClients:
+ index = userAgent.find(client)
+ if index != -1:
+ l = len(client)
+ endex = userAgent[index + l:].find(' ', index)
+ return userAgent[index:] if endex == -1 else userAgent[index:endex + l]
+
+ for quick, result in quickclients:
+ index = userAgent.find(quick)
+ if index != -1:
+ return result
+
+ return userAgent[:20]
Copied: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/test/test_methodDescriptor.py (from rev 10159, CalendarServer/trunk/calendarserver/test/test_methodDescriptor.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/test/test_methodDescriptor.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/test/test_methodDescriptor.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,67 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twisted.trial.unittest import TestCase
+from calendarserver.methodDescriptor import getAdjustedMethodName
+
+class MethodDescriptor(TestCase):
+ """
+ Tests for L{getAdjustedMethodName}.
+ """
+ def test_getAdjustedMethodName(self):
+ """
+ L{getAdjustedMethodName} returns the appropriate method.
+ """
+
+ data = (
+ ("PROPFIND", "/calendars/users/user01/", {}, "PROPFIND Calendar Home",),
+ ("PROPFIND", "/calendars/users/user01/", {"cached": "1"}, "PROPFIND cached Calendar Home",),
+ ("PROPFIND", "/calendars/users/user01/ABC/", {}, "PROPFIND Calendar",),
+ ("PROPFIND", "/calendars/users/user01/inbox/", {}, "PROPFIND Inbox",),
+ ("PROPFIND", "/addressbooks/users/user01/", {}, "PROPFIND Adbk Home",),
+ ("PROPFIND", "/addressbooks/users/user01/", {"cached": "1"}, "PROPFIND cached Adbk Home",),
+ ("PROPFIND", "/addressbooks/users/user01/ABC/", {}, "PROPFIND Adbk",),
+ ("PROPFIND", "/addressbooks/users/user01/inbox/", {}, "PROPFIND Adbk",),
+ ("PROPFIND", "/principals/users/user01/", {}, "PROPFIND Principals",),
+ ("PROPFIND", "/principals/users/user01/", {"cached": "1"}, "PROPFIND cached Principals",),
+ ("PROPFIND", "/.well-known/caldav", {}, "PROPFIND",),
+
+ ("REPORT(CalDAV:sync-collection)", "/calendars/users/user01/ABC/", {}, "REPORT cal-sync",),
+ ("REPORT(CalDAV:calendar-query)", "/calendars/users/user01/ABC/", {}, "REPORT cal-query",),
+
+ ("POST", "/calendars/users/user01/", {}, "POST Calendar Home",),
+ ("POST", "/calendars/users/user01/outbox/", {"recipients": "1"}, "POST Freebusy",),
+ ("POST", "/calendars/users/user01/outbox/", {}, "POST Outbox",),
+ ("POST", "/apns", {}, "POST apns",),
+
+ ("PUT", "/calendars/users/user01/calendar/1.ics", {}, "PUT ics",),
+ ("PUT", "/calendars/users/user01/calendar/1.ics", {"itip.requests": "1"}, "PUT Organizer",),
+ ("PUT", "/calendars/users/user01/calendar/1.ics", {"itip.reply": "1"}, "PUT Attendee",),
+
+ ("GET", "/calendars/users/user01/", {}, "GET Calendar Home",),
+ ("GET", "/calendars/users/user01/calendar/", {}, "GET Calendar",),
+ ("GET", "/calendars/users/user01/calendar/1.ics", {}, "GET ics",),
+
+ ("DELETE", "/calendars/users/user01/", {}, "DELETE Calendar Home",),
+ ("DELETE", "/calendars/users/user01/calendar/", {}, "DELETE Calendar",),
+ ("DELETE", "/calendars/users/user01/calendar/1.ics", {}, "DELETE ics",),
+ ("DELETE", "/calendars/users/user01/inbox/1.ics", {}, "DELETE inbox ics",),
+
+ ("ACL", "/calendars/users/user01/", {}, "ACL",),
+ )
+
+ for method, uri, extras, result in data:
+ self.assertEqual(getAdjustedMethodName(method, uri, extras), result, "Failed getAdjustedMethodName: %s" % (result,))
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/purge.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -44,7 +44,10 @@
from calendarserver.tools.cmdline import utilityMain
from calendarserver.tools.principals import removeProxy
+from calendarserver.tools import tables
+import collections
+
log = Logger()
DEFAULT_BATCH_SIZE = 100
@@ -61,7 +64,7 @@
print " -h --help: print this help and exit"
print " -f --config <path>: Specify caldavd.plist configuration path"
print " -d --days <number>: specify how many days in the past to retain (default=%d)" % (DEFAULT_RETAIN_DAYS,)
- #print " -b --batch <number>: number of events to remove in each transaction (default=%d)" % (DEFAULT_BATCH_SIZE,)
+ #print " -b --batch <number>: number of events to remove in each transaction (default=%d)" % (DEFAULT_BATCH_SIZE,)
print " -n --dry-run: calculate how many events to purge, but do not purge data"
print " -v --verbose: print progress information"
print ""
@@ -72,6 +75,8 @@
else:
sys.exit(0)
+
+
def usage_purge_orphaned_attachments(e=None):
name = os.path.basename(sys.argv[0])
@@ -82,7 +87,7 @@
print "options:"
print " -h --help: print this help and exit"
print " -f --config <path>: Specify caldavd.plist configuration path"
- #print " -b --batch <number>: number of attachments to remove in each transaction (default=%d)" % (DEFAULT_BATCH_SIZE,)
+ #print " -b --batch <number>: number of attachments to remove in each transaction (default=%d)" % (DEFAULT_BATCH_SIZE,)
print " -n --dry-run: calculate how many attachments to purge, but do not purge data"
print " -v --verbose: print progress information"
print ""
@@ -93,6 +98,8 @@
else:
sys.exit(0)
+
+
def usage_purge_principal(e=None):
name = os.path.basename(sys.argv[0])
@@ -115,11 +122,13 @@
sys.exit(0)
+
class WorkerService(Service):
def __init__(self, store):
self._store = store
+
def rootResource(self):
try:
rootResource = getRootResource(config, self._store)
@@ -209,7 +218,6 @@
-
def main_purge_events():
try:
@@ -285,6 +293,7 @@
)
+
def main_purge_orphaned_attachments():
try:
@@ -347,6 +356,7 @@
)
+
def main_purge_principals():
try:
@@ -401,13 +411,13 @@
PurgePrincipalService.verbose = verbose
PurgePrincipalService.doimplicit = doimplicit
-
utilityMain(
configFileName,
PurgePrincipalService
)
+
@inlineCallbacks
def purgeOldEvents(store, directory, root, date, batchSize, verbose=False,
dryrun=False):
@@ -462,16 +472,42 @@
print "(Dry run) Searching for orphaned attachments..."
txn = store.newTransaction(label="Find orphaned attachments")
orphans = (yield txn.orphanedAttachments())
- orphanCount = len(orphans)
if verbose:
- if orphanCount == 0:
- print "No orphaned attachments"
- elif orphanCount == 1:
- print "1 orphaned attachment"
- else:
- print "%d orphaned attachments" % (orphanCount,)
- returnValue(orphanCount)
+ # Print aggregate details by user
+ byuser = collections.defaultdict(int)
+ for owner_uid, _ignore_dropbox_id, _ignore_path, size in orphans:
+ byuser[owner_uid] += size
+ # Print table of results
+ table = tables.Table()
+ table.addHeader(("User", "Current Quota", "Orphaned Size", "Orphaned Count"))
+ table.setDefaultColumnFormats(
+ (
+ tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.LEFT_JUSTIFY),
+ tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ )
+ )
+ total = 0
+ for user, quota, size, count in sorted(orphans):
+ table.addRow((
+ user,
+ quota,
+ size,
+ count,
+ ))
+ total += count
+ table.addFooter(("Total:", "", "", total))
+
+ print "\n"
+ print "Orphaned Attachments by User:\n"
+ table.printTable()
+ else:
+ total = sum([x[3] for x in orphans])
+
+ returnValue(total)
+
if verbose:
print "Removing orphaned attachments..."
@@ -499,14 +535,12 @@
-
-
@inlineCallbacks
def purgeUIDs(store, directory, root, uids, verbose=False, dryrun=False,
completely=False, doimplicit=True):
total = 0
- allAssignments = { }
+ allAssignments = {}
for uid in uids:
count, allAssignments[uid] = (yield purgeUID(store, uid, directory, root,
@@ -607,7 +641,6 @@
main.removeProperty(exdate_rdate)
dirty = True
-
# Remove any overridden components beyond the cutoff
for component in tuple(event.subcomponents()):
if component.name() == "VEVENT":
@@ -625,6 +658,7 @@
return CANCELEVENT_NOT_MODIFIED
+
@inlineCallbacks
def purgeUID(store, uid, directory, root, verbose=False, dryrun=False, proxies=True,
when=None, completely=False, doimplicit=True):
@@ -686,7 +720,7 @@
# Anything in the past is left alone
whenString = when.getText()
- filter = caldavxml.Filter(
+ filter = caldavxml.Filter(
caldavxml.ComponentFilter(
caldavxml.ComponentFilter(
TimeRange(start=whenString,),
@@ -717,7 +751,7 @@
childNames.append(childName)
else:
# events matching filter
- for childName, childUid, childType in (yield collection.index().indexedSearch(filter)):
+ for childName, _ignore_childUid, _ignore_childType in (yield collection.index().indexedSearch(filter)):
childNames.append(childName)
for childName in childNames:
@@ -791,7 +825,6 @@
if incrementCount:
count += 1
-
txn = getattr(request, "_newStoreTransaction", None)
# Commit
if txn is not None:
@@ -834,12 +867,11 @@
if not dryrun:
(yield storeCalHome.remove())
-
# Remove VCards
storeAbHome = (yield txn.addressbookHomeWithUID(uid))
if storeAbHome is not None:
- for abColl in list( (yield storeAbHome.addressbooks()) ):
- for card in list( (yield abColl.addressbookObjects()) ):
+ for abColl in list((yield storeAbHome.addressbooks())):
+ for card in list((yield abColl.addressbookObjects())):
cardName = card.name()
if verbose:
uri = "/addressbooks/__uids__/%s/%s/%s" % (uid, abColl.name(), cardName)
@@ -884,6 +916,7 @@
returnValue((count, assignments))
+
@inlineCallbacks
def purgeProxyAssignments(principal):
@@ -904,4 +937,3 @@
(yield subPrincipal.writeProperty(davxml.GroupMemberSet(), None))
returnValue(assignments)
-
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_purge_old_events.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_purge_old_events.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/tools/test/test_purge_old_events.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -84,7 +84,7 @@
SEQUENCE:2
END:VEVENT
END:VCALENDAR
-""".replace("\n", "\r\n") % {"year":now-5}
+""".replace("\n", "\r\n") % {"year": now - 5}
OLD_ATTACHMENT_ICS = """BEGIN:VCALENDAR
VERSION:2.0
@@ -134,7 +134,7 @@
SEQUENCE:2
END:VEVENT
END:VCALENDAR
-""".replace("\n", "\r\n") % {"year":now-5}
+""".replace("\n", "\r\n") % {"year": now - 5}
ENDLESS_ICS = """BEGIN:VCALENDAR
VERSION:2.0
@@ -183,7 +183,7 @@
SEQUENCE:4
END:VEVENT
END:VCALENDAR
-""".replace("\n", "\r\n") % {"year":now-5}
+""".replace("\n", "\r\n") % {"year": now - 5}
REPEATING_AWHILE_ICS = """BEGIN:VCALENDAR
VERSION:2.0
@@ -232,7 +232,7 @@
SEQUENCE:6
END:VEVENT
END:VCALENDAR
-""".replace("\n", "\r\n") % {"year":now-5}
+""".replace("\n", "\r\n") % {"year": now - 5}
STRADDLING_ICS = """BEGIN:VCALENDAR
VERSION:2.0
@@ -267,7 +267,7 @@
SEQUENCE:5
END:VEVENT
END:VCALENDAR
-""".replace("\n", "\r\n") % {"year":now-2, "until":now+1}
+""".replace("\n", "\r\n") % {"year": now - 2, "until": now + 1}
RECENT_ICS = """BEGIN:VCALENDAR
VERSION:2.0
@@ -301,7 +301,7 @@
SEQUENCE:2
END:VEVENT
END:VCALENDAR
-""".replace("\n", "\r\n") % {"year":now}
+""".replace("\n", "\r\n") % {"year": now}
VCARD_1 = """BEGIN:VCARD
@@ -421,7 +421,7 @@
count = (yield txn.removeOldEvents(cutoff))
self.assertEquals(count, 3)
results = (yield txn.eventsOlderThan(cutoff))
- self.assertEquals(results, [ ])
+ self.assertEquals(results, [])
# Remove oldest events (none left)
count = (yield txn.removeOldEvents(cutoff))
@@ -470,6 +470,7 @@
# Just look for orphaned attachments but don't delete
orphans = (yield txn.orphanedAttachments())
self.assertEquals(len(orphans), 1)
+ self.assertEquals(orphans, [["home1", 19, 19, 1]])
# Remove orphaned attachments, should be 1
count = (yield txn.removeOrphanedAttachments(batchSize=100))
@@ -515,12 +516,12 @@
abColl = (yield abHome.addressbookWithName("addressbook"))
(yield abColl.createAddressBookObjectWithName("card1",
VCardComponent.fromString(VCARD_1)))
- self.assertEquals(len( (yield abColl.addressbookObjects()) ), 1)
+ self.assertEquals(len((yield abColl.addressbookObjects())), 1)
# Verify there are 3 events in calendar1
calHome = (yield txn.calendarHomeWithUID("home1"))
calColl = (yield calHome.calendarWithName("calendar1"))
- self.assertEquals(len( (yield calColl.calendarObjects()) ), 3)
+ self.assertEquals(len((yield calColl.calendarObjects())), 3)
# Make the newly created objects available to the purgeUID transaction
(yield txn.commit())
@@ -540,7 +541,7 @@
calHome = (yield txn.calendarHomeWithUID("home1"))
calColl = (yield calHome.calendarWithName("calendar1"))
- self.assertEquals(len( (yield calColl.calendarObjects()) ), 2)
+ self.assertEquals(len((yield calColl.calendarObjects())), 2)
@inlineCallbacks
@@ -552,12 +553,12 @@
abColl = (yield abHome.addressbookWithName("addressbook"))
(yield abColl.createAddressBookObjectWithName("card1",
VCardComponent.fromString(VCARD_1)))
- self.assertEquals(len( (yield abColl.addressbookObjects()) ), 1)
+ self.assertEquals(len((yield abColl.addressbookObjects())), 1)
# Verify there are 3 events in calendar1
calHome = (yield txn.calendarHomeWithUID("home1"))
calColl = (yield calHome.calendarWithName("calendar1"))
- self.assertEquals(len( (yield calColl.calendarObjects()) ), 3)
+ self.assertEquals(len((yield calColl.calendarObjects())), 3)
# Make the newly created objects available to the purgeUID transaction
(yield txn.commit())
@@ -601,4 +602,3 @@
total = (yield purgeOrphanedAttachments(self._sqlCalendarStore, 2,
dryrun=False, verbose=False))
self.assertEquals(total, 0)
-
Modified: CalendarServer/branches/users/gaya/sharedgroups/calendarserver/webcal/resource.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/calendarserver/webcal/resource.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/calendarserver/webcal/resource.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -40,7 +40,7 @@
from twisted.internet.defer import succeed
-from calendarserver.platform.darwin.timezone import getLocalTimezone
+from twext.python.timezone import getLocalTimezone
class WebCalendarResource (ReadOnlyResourceMixIn, DAVFile):
Modified: CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-test.plist 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/conf/caldavd-test.plist 2012-12-12 22:11:41 UTC (rev 10162)
@@ -901,6 +901,10 @@
<key>EnableDropBox</key>
<true/>
+ <!-- Calendar Managed Attachments -->
+ <key>EnableManagedAttachments</key>
+ <true/>
+
<!-- Private Events -->
<key>EnablePrivateEvents</key>
<true/>
Modified: CalendarServer/branches/users/gaya/sharedgroups/contrib/tools/readStats.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/contrib/tools/readStats.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/contrib/tools/readStats.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -54,23 +54,23 @@
-def printStats(stats, multimode, showMethods, topUsers):
+def printStats(stats, multimode, showMethods, topUsers, showAgents):
if len(stats) == 1:
if "Failed" in stats[0]:
printFailedStats(stats[0]["Failed"])
else:
try:
- printStat(stats[0], multimode[0], showMethods, topUsers)
+ printStat(stats[0], multimode[0], showMethods, topUsers, showAgents)
except KeyError, e:
printFailedStats("Unable to find key '%s' in statistics from server socket" % (e,))
sys.exit(1)
else:
- printMultipleStats(stats, multimode, showMethods, topUsers)
+ printMultipleStats(stats, multimode, showMethods, topUsers, showAgents)
-def printStat(stats, index, showMethods, topUsers):
+def printStat(stats, index, showMethods, topUsers, showAgents):
print "- " * 40
print "Server: %s" % (stats["Server"],)
@@ -91,15 +91,17 @@
print "Current Memory Used: Unavailable"
print
printRequestSummary(stats)
- printHistogramSummary(stats[index])
+ printHistogramSummary(stats[index], index)
if showMethods:
printMethodCounts(stats[index])
if topUsers:
printUserCounts(stats[index], topUsers)
+ if showAgents:
+ printAgentCounts(stats[index])
-def printMultipleStats(stats, multimode, showMethods, topUsers):
+def printMultipleStats(stats, multimode, showMethods, topUsers, showAgents):
labels = serverLabels(stats)
@@ -130,6 +132,8 @@
printMultiMethodCounts(stats, multimode[0])
if topUsers:
printMultiUserCounts(stats, multimode[0], topUsers)
+ if showAgents:
+ printMultiAgentCounts(stats, multimode[0])
@@ -274,9 +278,9 @@
-def printHistogramSummary(stat):
+def printHistogramSummary(stat, index):
- print "5 minute average response histogram"
+ print "%s average response histogram" % (index,)
table = tables.Table()
table.addHeader(
("", "<10ms", "10ms<->100ms", "100ms<->1s", "1s<->10s", "10s<->30s", "30s<->60s", ">60s", "Over 1s", "Over 10s"),
@@ -329,41 +333,7 @@
for k in keys[1:]:
totals[i][k] += stat[index][i][k]
- print "%s average response histogram" % (index,)
- table = tables.Table()
- table.addHeader(
- ("", "<10ms", "10ms<->100ms", "100ms<->1s", "1s<->10s", "10s<->30s", "30s<->60s", ">60s", "Over 1s", "Over 10s"),
- )
- table.setDefaultColumnFormats(
- (
- tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.LEFT_JUSTIFY),
- tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%d (%.1f%%)", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- )
- )
- for i in ("T", "T-RESP-WR",):
- table.addRow((
- "Overall Response" if i == "T" else "Response Write",
- (totals[i]["<10ms"], safeDivision(totals[i]["<10ms"], totals[i]["requests"], 100.0)),
- (totals[i]["10ms<->100ms"], safeDivision(totals[i]["10ms<->100ms"], totals[i]["requests"], 100.0)),
- (totals[i]["100ms<->1s"], safeDivision(totals[i]["100ms<->1s"], totals[i]["requests"], 100.0)),
- (totals[i]["1s<->10s"], safeDivision(totals[i]["1s<->10s"], totals[i]["requests"], 100.0)),
- (totals[i]["10s<->30s"], safeDivision(totals[i]["10s<->30s"], totals[i]["requests"], 100.0)),
- (totals[i]["30s<->60s"], safeDivision(totals[i]["30s<->60s"], totals[i]["requests"], 100.0)),
- (totals[i][">60s"], safeDivision(totals[i][">60s"], totals[i]["requests"], 100.0)),
- safeDivision(totals[i]["Over 1s"], totals[i]["requests"], 100.0),
- safeDivision(totals[i]["Over 10s"], totals[i]["requests"], 100.0),
- ))
- os = StringIO()
- table.printTable(os=os)
- print os.getvalue()
+ printHistogramSummary(totals, index)
@@ -372,22 +342,38 @@
print "Method Counts"
table = tables.Table()
table.addHeader(
- ("Method", "Total", "Percentage"),
+ ("Method", "Count", "%", "Av. Response", "%", "Total Resp. %"),
)
+ table.addHeader(
+ ("", "", "", "(ms)", "", ""),
+ )
table.setDefaultColumnFormats(
(
tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.LEFT_JUSTIFY),
tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
)
)
- total = sum(stat["method"].values())
+ response_average = {}
+ for method in stat["method"].keys():
+ response_average[method] = stat["method-t"][method] / stat["method"][method]
+
+ total_count = sum(stat["method"].values())
+ total_avresponse = sum(response_average.values())
+ total_response = sum(stat["method-t"].values())
+
for method in sorted(stat["method"].keys()):
table.addRow((
method,
stat["method"][method],
- safeDivision(stat["method"][method], total, 100.0),
+ safeDivision(stat["method"][method], total_count, 100.0),
+ response_average[method],
+ safeDivision(response_average[method], total_avresponse, 100.0),
+ safeDivision(stat["method-t"][method], total_response, 100.0),
))
os = StringIO()
table.printTable(os=os)
@@ -398,36 +384,17 @@
def printMultiMethodCounts(stats, index):
methods = collections.defaultdict(int)
+ method_times = collections.defaultdict(float)
for stat in stats:
for method in stat[index]["method"]:
methods[method] += stat[index]["method"][method]
+ for method_time in stat[index]["method-t"]:
+ method_times[method_time] += stat[index]["method-t"][method_time]
- print "Method Counts"
- table = tables.Table()
- table.addHeader(
- ("Method", "Total", "Percentage"),
- )
- table.setDefaultColumnFormats(
- (
- tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.LEFT_JUSTIFY),
- tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
- )
- )
+ printMethodCounts({"method": methods, "method-t": method_times})
- total = sum(methods.values())
- for method in sorted(methods.keys()):
- table.addRow((
- method,
- methods[method],
- safeDivision(methods[method], total, 100.0),
- ))
- os = StringIO()
- table.printTable(os=os)
- print os.getvalue()
-
def printUserCounts(stat, topUsers):
print "User Counts"
@@ -444,11 +411,11 @@
)
total = sum(stat["uid"].values())
- for uid in sorted(stat["uid"].items(), key=lambda x: x[1], reverse=True)[:topUsers]:
+ for uid, value in sorted(stat["uid"].items(), key=lambda x: x[1], reverse=True)[:topUsers]:
table.addRow((
uid,
- stat["uid"][uid],
- safeDivision(stat["uid"][uid], total, 100.0),
+ value,
+ safeDivision(value, total, 100.0),
))
os = StringIO()
table.printTable(os=os)
@@ -463,25 +430,31 @@
for uid in stat[index]["uid"]:
uids[uid] += stat[index]["uid"][uid]
- print "User Counts"
+ printUserCounts({"uid": uids}, topUsers)
+
+
+
+def printAgentCounts(stat):
+
+ print "User-Agent Counts"
table = tables.Table()
table.addHeader(
- ("User", "Total", "Percentage"),
+ ("User-Agent", "Total", "Percentage"),
)
table.setDefaultColumnFormats(
- (
+ (
tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.LEFT_JUSTIFY),
tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
tables.Table.ColumnFormat("%.1f%%", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
)
)
- total = sum(uids.values())
- for uid, count in sorted(uids.items(), key=lambda x: x[1], reverse=True)[:topUsers]:
+ total = sum(stat["user-agent"].values())
+ for ua in sorted(stat["user-agent"].keys()):
table.addRow((
- uid,
- count,
- safeDivision(count, total, 100.0),
+ ua,
+ stat["user-agent"][ua],
+ safeDivision(stat["user-agent"][ua], total, 100.0),
))
os = StringIO()
table.printTable(os=os)
@@ -489,6 +462,17 @@
+def printMultiAgentCounts(stats, index):
+
+ uas = collections.defaultdict(int)
+ for stat in stats:
+ for ua in stat[index]["user-agent"]:
+ uas[ua] += stat[index]["user-agent"][ua]
+
+ printUserCounts({"user-agent": uas})
+
+
+
def usage(error_msg=None):
if error_msg:
print error_msg
@@ -505,6 +489,7 @@
--60 Display multiserver 1 hour average
--methods Include details about HTTP method usage
--users N Include details about top N users
+ --agents Include details about HTTP User-Agent usage
Description:
This utility will print a summary of statistics read from a
@@ -525,11 +510,12 @@
useTCP = False
showMethods = False
topUsers = 0
+ showAgents = False
multimodes = (("Current", 60,), ("1 Minute", 60,), ("5 Minutes", 5 * 60,), ("1 Hour", 60 * 60,),)
multimode = multimodes[2]
- options, args = getopt.getopt(sys.argv[1:], "hs:t:", ["tcp=", "0", "1", "5", "60", "methods", "users="])
+ options, args = getopt.getopt(sys.argv[1:], "hs:t:", ["tcp=", "0", "1", "5", "60", "methods", "users=", "agents"])
for option, value in options:
if option == "-h":
@@ -553,7 +539,9 @@
showMethods = True
elif option == "--users":
topUsers = int(value)
+ elif option == "--agents":
+ showAgents = True
while True:
- printStats([readSock(server, useTCP) for server in servers], multimode, showMethods, topUsers)
+ printStats([readSock(server, useTCP) for server in servers], multimode, showMethods, topUsers, showAgents)
time.sleep(delay)
Modified: CalendarServer/branches/users/gaya/sharedgroups/support/build.sh
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/support/build.sh 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/support/build.sh 2012-12-12 22:11:41 UTC (rev 10162)
@@ -254,6 +254,8 @@
}
if [ ! -f "${cache_file}" ]; then
+ echo "No cache file: ${cache_file}";
+
echo "Downloading ${name}...";
local pkg_host="static.calendarserver.org";
Modified: CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/dal/syntax.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/dal/syntax.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/twext/enterprise/dal/syntax.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -41,6 +41,8 @@
except ImportError:
cx_Oracle = None
+
+
class DALError(Exception):
"""
Base class for exceptions raised by this module. This can be raised
@@ -327,8 +329,8 @@
# 0'.)
__add__ = comparison("+")
__sub__ = comparison("-")
- __div__= comparison("/")
- __mul__= comparison("*")
+ __div__ = comparison("/")
+ __mul__ = comparison("*")
def __nonzero__(self):
@@ -365,6 +367,8 @@
def Contains(self, other):
return CompoundComparison(self, "like", CompoundComparison(Constant('%'), '||', CompoundComparison(Constant(other), '||', Constant('%'))))
+
+
class FunctionInvocation(ExpressionSyntax):
def __init__(self, function, *args):
self.function = function
@@ -453,6 +457,7 @@
Count = Function("count")
+Sum = Function("sum")
Max = Function("max")
Len = Function("character_length", "length")
Upper = Function("upper")
@@ -780,23 +785,28 @@
return self.model.table.name + '.' + name
+
class ResultAliasSyntax(ExpressionSyntax):
def __init__(self, expression, alias=None):
self.expression = expression
self.alias = alias
+
def aliasName(self, queryGenerator):
if self.alias is None:
self.alias = queryGenerator.nextGeneratedID()
return self.alias
+
def columnReference(self):
return AliasReferenceSyntax(self)
+
def allColumns(self):
return self.expression.allColumns()
+
def subSQL(self, queryGenerator, allTables):
result = SQLFragment()
result.append(self.expression.subSQL(queryGenerator, allTables))
@@ -804,18 +814,22 @@
return result
+
class AliasReferenceSyntax(ExpressionSyntax):
def __init__(self, resultAlias):
self.resultAlias = resultAlias
+
def allColumns(self):
return self.resultAlias.allColumns()
+
def subSQL(self, queryGenerator, allTables):
return SQLFragment(self.resultAlias.aliasName(queryGenerator))
+
class AliasedColumnSyntax(ColumnSyntax):
"""
An L{AliasedColumnSyntax} is like a L{ColumnSyntax}, but it generates SQL
@@ -898,9 +912,9 @@
def subSQL(self, queryGenerator, allTables):
- if ( queryGenerator.dialect == ORACLE_DIALECT
+ if (queryGenerator.dialect == ORACLE_DIALECT
and isinstance(self.b, Constant) and self.b.value == ''
- and self.op in ('=', '!=') ):
+ and self.op in ('=', '!=')):
return NullComparison(self.a, self.op).subSQL(queryGenerator, allTables)
stmt = SQLFragment()
result = self._subexpression(self.a, queryGenerator, allTables)
@@ -948,6 +962,7 @@
def __init__(self):
self.name = "*"
+
def allColumns(self):
return []
@@ -1024,6 +1039,7 @@
return self.columns
+
class SetExpression(object):
"""
A UNION, INTERSECT, or EXCEPT construct used inside a SELECT.
@@ -1052,6 +1068,7 @@
if self.optype not in (None, SetExpression.OPTYPE_ALL, SetExpression.OPTYPE_DISTINCT,):
raise DALError("Must have either 'all' or 'distinct' in a set expression")
+
def subSQL(self, queryGenerator, allTables):
result = SQLFragment()
for select in self.selects:
@@ -1063,9 +1080,12 @@
result.append(select.subSQL(queryGenerator, allTables))
return result
+
def allColumns(self):
return []
+
+
class Union(SetExpression):
"""
A UNION construct used inside a SELECT.
@@ -1073,6 +1093,8 @@
def setOpSQL(self, queryGenerator):
return SQLFragment(" UNION ")
+
+
class Intersect(SetExpression):
"""
An INTERSECT construct used inside a SELECT.
@@ -1080,6 +1102,8 @@
def setOpSQL(self, queryGenerator):
return SQLFragment(" INTERSECT ")
+
+
class Except(SetExpression):
"""
An EXCEPT construct used inside a SELECT.
@@ -1092,6 +1116,8 @@
else:
raise NotImplementedError("Unsupported dialect")
+
+
class Select(_Statement):
"""
'select' statement.
@@ -1131,6 +1157,7 @@
if self.From.As is None:
self.From.As = ""
+
def __eq__(self, other):
"""
Create a comparison.
@@ -1238,6 +1265,7 @@
for column in self.columns.columns:
yield column
+
def tables(self):
"""
Determine the tables used by the result columns.
@@ -1255,6 +1283,7 @@
return [TableSyntax(table) for table in tables]
+
def _commaJoined(stmts):
first = True
cstatement = SQLFragment()
@@ -1663,6 +1692,7 @@
return SQLFragment('savepoint %s' % (self.name,))
+
class RollbackToSavepoint(_LockingStatement):
"""
An SQL 'rollback to savepoint' statement.
@@ -1676,6 +1706,7 @@
return SQLFragment('rollback to savepoint %s' % (self.name,))
+
class ReleaseSavepoint(_LockingStatement):
"""
An SQL 'release savepoint' statement.
@@ -1821,4 +1852,3 @@
# (Although this is a special keyword in a CREATE statement, in an INSERT it
# behaves like an expression to the best of my knowledge.)
default = NamedValue('default')
-
Copied: CalendarServer/branches/users/gaya/sharedgroups/twext/python/test/test_timezone.py (from rev 10159, CalendarServer/trunk/twext/python/test/test_timezone.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twext/python/test/test_timezone.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/twext/python/test/test_timezone.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,65 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twistedcaldav.test.util import TestCase
+from twistedcaldav.config import config
+import twext.python.timezone
+import twistedcaldav.timezones
+from twext.python.timezone import getLocalTimezone, DEFAULT_TIMEZONE
+
+class DefaultTimezoneTests(TestCase):
+
+ def stubLookup(self):
+ return self._storedLookup
+
+ def stubHasTZ(self, ignored):
+ return self._storedHasTZ.pop()
+
+ def setUp(self):
+ self.patch(twext.python.timezone, "lookupSystemTimezone", self.stubLookup)
+ self.patch(twistedcaldav.timezones,
+ "hasTZ", self.stubHasTZ)
+
+ def test_getLocalTimezone(self):
+
+ # Empty config, system timezone known = use system timezone
+ self.patch(config, "DefaultTimezone", "")
+ self._storedLookup = "America/New_York"
+ self._storedHasTZ = [True]
+ self.assertEquals(getLocalTimezone(), "America/New_York")
+
+ # Empty config, system timezone unknown = use DEFAULT_TIMEZONE
+ self.patch(config, "DefaultTimezone", "")
+ self._storedLookup = "Unknown/Unknown"
+ self._storedHasTZ = [False]
+ self.assertEquals(getLocalTimezone(), DEFAULT_TIMEZONE)
+
+ # Known config value = use config value
+ self.patch(config, "DefaultTimezone", "America/New_York")
+ self._storedHasTZ = [True]
+ self.assertEquals(getLocalTimezone(), "America/New_York")
+
+ # Unknown config value, system timezone known = use system timezone
+ self.patch(config, "DefaultTimezone", "Unknown/Unknown")
+ self._storedLookup = "America/New_York"
+ self._storedHasTZ = [True, False]
+ self.assertEquals(getLocalTimezone(), "America/New_York")
+
+ # Unknown config value, system timezone unknown = use DEFAULT_TIMEZONE
+ self.patch(config, "DefaultTimezone", "Unknown/Unknown")
+ self._storedLookup = "Unknown/Unknown"
+ self._storedHasTZ = [False, False]
+ self.assertEquals(getLocalTimezone(), DEFAULT_TIMEZONE)
Copied: CalendarServer/branches/users/gaya/sharedgroups/twext/python/timezone.py (from rev 10159, CalendarServer/trunk/twext/python/timezone.py)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twext/python/timezone.py (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/twext/python/timezone.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,51 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+from twistedcaldav.config import config
+import twistedcaldav.timezones
+
+DEFAULT_TIMEZONE = "America/Los_Angeles"
+
+try:
+ from Foundation import NSTimeZone
+ def lookupSystemTimezone():
+ return NSTimeZone.localTimeZone().name().encode("utf-8")
+
+except:
+ def lookupSystemTimezone():
+ return ""
+
+
+def getLocalTimezone():
+ """
+ Returns the default timezone for the server. The order of precedence is:
+ config.DefaultTimezone, lookupSystemTimezone( ), DEFAULT_TIMEZONE.
+ Also, if neither of the first two values in that list are in the timezone
+ database, DEFAULT_TIMEZONE is returned.
+
+ @return: The server's local timezone name
+ @rtype: C{str}
+ """
+ if config.DefaultTimezone:
+ if twistedcaldav.timezones.hasTZ(config.DefaultTimezone):
+ return config.DefaultTimezone
+
+ systemTimezone = lookupSystemTimezone()
+ if twistedcaldav.timezones.hasTZ(systemTimezone):
+ return systemTimezone
+
+ return DEFAULT_TIMEZONE
Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/stdconfig.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -525,7 +525,7 @@
"EnableWellKnown" : True, # /.well-known resource
"EnableCalendarQueryExtended" : True, # Extended calendar-query REPORT
- "EnableManagedAttachments" : True, # Support Managed Attachments
+ "EnableManagedAttachments" : False, # Support Managed Attachments
#
# Non-standard CalDAV extensions
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/file.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/file.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -89,7 +89,6 @@
self._childClass = Calendar
-
createCalendarWithName = CommonHome.createChildWithName
removeCalendarWithName = CommonHome.removeChildWithName
@@ -139,6 +138,7 @@
returnValue(False)
+
@inlineCallbacks
def getCalendarResourcesForUID(self, uid, allow_shared=False):
@@ -150,6 +150,7 @@
returnValue(results)
+
@inlineCallbacks
def calendarObjectWithDropboxID(self, dropboxID):
"""
@@ -197,6 +198,7 @@
self.createCalendarWithName("inbox")
+
def ensureDefaultCalendarsExist(self):
"""
Double check that we have calendars supporting at least VEVENT and VTODO,
@@ -225,6 +227,8 @@
_requireCalendarWithType("VEVENT", "calendar")
_requireCalendarWithType("VTODO", "tasks")
+
+
class Calendar(CommonHomeChild):
"""
File-based implementation of L{ICalendar}.
@@ -289,10 +293,12 @@
elif pname in self.properties():
del self.properties()[pname]
+
def getSupportedComponents(self):
result = str(self.properties().get(PropertyName.fromElement(customxml.TwistedCalendarSupportedComponents), ""))
return result if result else None
+
def isSupportedComponent(self, componentType):
supported = self.getSupportedComponents()
if supported:
@@ -300,6 +306,7 @@
else:
return True
+
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
@@ -313,12 +320,14 @@
),
)
+
def contentType(self):
"""
The content type of Calendar objects is text/calendar.
"""
return MimeType.fromString("text/calendar; charset=utf-8")
+
def splitCollectionByComponentTypes(self):
"""
If the calendar contains iCalendar data with different component types, then split it into separate collections
@@ -329,22 +338,24 @@
# TODO: implement this for filestore
pass
+
def _countComponentTypes(self):
"""
Count each component type in this calendar.
-
- @return: a C{tuple} of C{tuple} containing the component type name and count.
+
+ @return: a C{tuple} of C{tuple} containing the component type name and count.
"""
rows = self._index._oldIndex.componentTypeCounts()
result = tuple([(componentType, componentCount) for componentType, componentCount in sorted(rows, key=lambda x:x[0])])
return result
+
def _splitComponentType(self, component):
"""
Create a new calendar and move all components of the specified component type into the new one.
Make sure properties and sharing state is preserved on the new calendar.
-
+
@param component: Component type to split out
@type component: C{str}
"""
@@ -352,6 +363,7 @@
# TODO: implement this for filestore
pass
+
def _transferSharingDetails(self, newcalendar, component):
"""
If the current calendar is shared, make the new calendar shared in the same way, but tweak the name.
@@ -360,6 +372,7 @@
# TODO: implement this for filestore
pass
+
def _transferCalendarObjects(self, newcalendar, component):
"""
Move all calendar components of the specified type to the specified calendar.
@@ -522,19 +535,23 @@
self._objectText = text
return text
+
def uid(self):
if not hasattr(self, "_uid"):
self._uid = self.component().resourceUID()
return self._uid
+
def componentType(self):
if not hasattr(self, "_componentType"):
self._componentType = self.component().mainType()
return self._componentType
+
def organizer(self):
return self.component().getOrganizer()
+
def getMetadata(self):
metadata = {}
metadata["accessMode"] = self.accessMode
@@ -544,9 +561,11 @@
metadata["hasPrivateComment"] = self.hasPrivateComment
return metadata
+
def _get_accessMode(self):
return str(self.properties().get(PropertyName.fromElement(customxml.TwistedCalendarAccessProperty), ""))
+
def _set_accessMode(self, value):
pname = PropertyName.fromElement(customxml.TwistedCalendarAccessProperty)
if value:
@@ -566,6 +585,7 @@
prop = str(prop) == "true"
return prop
+
def _set_isScheduleObject(self, value):
pname = PropertyName.fromElement(customxml.TwistedSchedulingObjectResource)
if value is not None:
@@ -578,6 +598,7 @@
def _get_scheduleTag(self):
return str(self.properties().get(PropertyName.fromElement(caldavxml.ScheduleTag), ""))
+
def _set_scheduleTag(self, value):
pname = PropertyName.fromElement(caldavxml.ScheduleTag)
if value:
@@ -590,6 +611,7 @@
def _get_scheduleEtags(self):
return tuple([str(etag) for etag in self.properties().get(PropertyName.fromElement(customxml.TwistedScheduleMatchETags), customxml.TwistedScheduleMatchETags()).children])
+
def _set_scheduleEtags(self, value):
if value:
etags = [davxml.GETETag.fromString(etag) for etag in value]
@@ -605,6 +627,7 @@
def _get_hasPrivateComment(self):
return PropertyName.fromElement(customxml.TwistedCalendarHasPrivateCommentsProperty) in self.properties()
+
def _set_hasPrivateComment(self, value):
pname = PropertyName.fromElement(customxml.TwistedCalendarHasPrivateCommentsProperty)
if value:
@@ -722,6 +745,7 @@
),
)
+
# IDataStoreObject
def contentType(self):
"""
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/test/test_sql.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/caldav/datastore/test/test_sql.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -63,8 +63,9 @@
self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
yield self.populate()
- self.nowYear = {"now":PyCalendarDateTime.getToday().getYear()}
+ self.nowYear = {"now": PyCalendarDateTime.getToday().getYear()}
+
@inlineCallbacks
def populate(self):
yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
@@ -354,6 +355,7 @@
END:VCALENDAR
""".replace("\n", "\r\n") % self.nowYear)
+
@inlineCallbacks
def test_migrateDuplicateAttachmentsCalendarFromFile(self):
"""
@@ -371,6 +373,7 @@
self.assertEqual(ok, 3)
self.assertEqual(bad, 0)
+
@inlineCallbacks
def test_migrateCalendarFromFile_Transparency(self):
"""
@@ -403,6 +406,7 @@
self.assertEquals(uid, "uid4")
self.assertEquals(transp, 'T')
+
@inlineCallbacks
def test_migrateHomeFromFile(self):
"""
@@ -470,6 +474,7 @@
self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
+
@inlineCallbacks
def test_migrateHomeNoSplits(self):
"""
@@ -497,6 +502,7 @@
self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
+
def test_calendarHomeVersion(self):
"""
The DATAVERSION column for new calendar homes must match the
@@ -695,6 +701,7 @@
yield d1
yield d2
+
@inlineCallbacks
def test_datetimes(self):
calendarStore = self._sqlCalendarStore
@@ -714,6 +721,7 @@
self.assertEqual(obj.created(), datetimeMktime(datetime.datetime(2011, 2, 7, 11, 22, 47)))
self.assertEqual(obj.modified(), datetimeMktime(datetime.datetime(2011, 2, 8, 11, 22, 47)))
+
@inlineCallbacks
def test_notificationsProvisioningConcurrency(self):
"""
@@ -753,6 +761,7 @@
self.assertNotEqual(notification_uid1_1, None)
self.assertNotEqual(notification_uid1_2, None)
+
@inlineCallbacks
def test_removeCalendarPropertiesOnDelete(self):
"""
@@ -794,6 +803,7 @@
self.assertEqual(len(tuple(rows)), 0)
yield self.commit()
+
@inlineCallbacks
def test_removeCalendarObjectPropertiesOnDelete(self):
"""
@@ -839,6 +849,7 @@
self.assertEqual(len(tuple(rows)), 0)
yield self.commit()
+
@inlineCallbacks
def test_removeInboxObjectPropertiesOnDelete(self):
"""
@@ -891,6 +902,7 @@
self.assertEqual(len(tuple(rows)), 0)
yield self.commit()
+
@inlineCallbacks
def test_directShareCreateConcurrency(self):
"""
@@ -942,6 +954,7 @@
yield d1
yield d2
+
@inlineCallbacks
def test_transferSharingDetails(self):
"""
@@ -982,6 +995,7 @@
self.assertTrue(sharedCalendar is not None)
self.assertEqual(sharedCalendar._resourceID, newcalendar._resourceID)
+
@inlineCallbacks
def test_moveCalendarObjectResource(self):
"""
@@ -1003,6 +1017,7 @@
child = yield calendar1.calendarObjectWithName("5.ics")
self.assertTrue(child is not None)
+
@inlineCallbacks
def test_splitCalendars(self):
"""
@@ -1073,6 +1088,7 @@
self.assertTrue(pkey in calendar2.properties())
self.assertEqual(str(calendar2.properties()[pkey]), "A birthday calendar")
+
@inlineCallbacks
def test_noSplitCalendars(self):
"""
@@ -1099,6 +1115,7 @@
self.assertEqual(supported_components, set(("VEVENT", "VTODO",)))
+
@inlineCallbacks
def test_resourceLock(self):
"""
@@ -1176,6 +1193,7 @@
self.assertEqual(rMin, None)
self.assertEqual(rMax, None)
+
@inlineCallbacks
def test_notExpandedWithin(self):
"""
@@ -1292,8 +1310,8 @@
yield calendarObject.setComponent(component)
instances2 = yield calendarObject.instances()
self.assertNotEqual(
- sorted(instances, key=lambda x:x[0])[0],
- sorted(instances2, key=lambda x:x[0])[0],
+ sorted(instances, key=lambda x: x[0])[0],
+ sorted(instances2, key=lambda x: x[0])[0],
)
yield self.commit()
@@ -1304,13 +1322,14 @@
yield calendarObject.setComponent(component)
instances3 = yield calendarObject.instances()
self.assertEqual(
- sorted(instances2, key=lambda x:x[0])[0],
- sorted(instances3, key=lambda x:x[0])[0],
+ sorted(instances2, key=lambda x: x[0])[0],
+ sorted(instances3, key=lambda x: x[0])[0],
)
yield calendar.removeCalendarObjectWithName("indexing.ics")
yield self.commit()
+
@inlineCallbacks
def test_loadObjectResourcesWithName(self):
"""
@@ -1390,17 +1409,18 @@
self.assertEqual(resources[0].properties()[PropertyName.fromElement(prop)], prop)
resources = yield inbox.objectResourcesWithNames(("1.ics", "2.ics",))
- resources.sort(key=lambda x:x._name)
+ resources.sort(key=lambda x: x._name)
prop = caldavxml.CalendarDescription.fromString("p1")
self.assertEqual(resources[0].properties()[PropertyName.fromElement(prop)], prop)
prop = caldavxml.CalendarDescription.fromString("p2")
self.assertEqual(resources[1].properties()[PropertyName.fromElement(prop)], prop)
resources = yield inbox.objectResourcesWithNames(("bogus1.ics", "2.ics",))
- resources.sort(key=lambda x:x._name)
+ resources.sort(key=lambda x: x._name)
prop = caldavxml.CalendarDescription.fromString("p2")
self.assertEqual(resources[0].properties()[PropertyName.fromElement(prop)], prop)
+
@inlineCallbacks
def test_objectResourceWithID(self):
"""
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/file.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/file.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -61,7 +61,6 @@
import os
import types
-
ECALENDARTYPE = 0
EADDRESSBOOKTYPE = 1
@@ -531,7 +530,6 @@
return SharedCollectionRecord(*[str(item) if type(item) == types.UnicodeType else item for item in row])
-
class CommonHome(FileMetaDataMixin, LoggingMixIn):
# All these need to be initialized by derived classes for each store type
@@ -1285,8 +1283,6 @@
yield None
returnValue([])
-
-
class CommonObjectResource(FileMetaDataMixin, LoggingMixIn, FancyEqMixin):
"""
@ivar _path: The path of the file on disk
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -74,7 +74,7 @@
from twext.enterprise.dal.syntax import \
Delete, utcNowSQL, Union, Insert, Len, Max, Parameter, SavepointAction, \
- Select, Update, ColumnSyntax, TableSyntax, Upper, Count, ALL_COLUMNS
+ Select, Update, ColumnSyntax, TableSyntax, Upper, Count, ALL_COLUMNS, Sum
from twistedcaldav.config import config
@@ -969,39 +969,63 @@
returnValue(count)
- def _orphanedBase(limited): #@NoSelf
+ def _orphanedSummary(limited): #@NoSelf
at = schema.ATTACHMENT
co = schema.CALENDAR_OBJECT
+ ch = schema.CALENDAR_HOME
+ chm = schema.CALENDAR_HOME_METADATA
kwds = {}
if limited:
kwds["Limit"] = Parameter('batchSize')
return Select(
- [at.DROPBOX_ID, at.PATH],
- From=at.join(co, at.DROPBOX_ID == co.DROPBOX_ID, "left outer"),
+ [ch.OWNER_UID, chm.QUOTA_USED_BYTES, Sum(at.SIZE), Count(at.DROPBOX_ID)],
+ From=at.join(
+ co, at.DROPBOX_ID == co.DROPBOX_ID, "left outer").join(
+ ch, at.CALENDAR_HOME_RESOURCE_ID == ch.RESOURCE_ID).join(
+ chm, ch.RESOURCE_ID == chm.RESOURCE_ID
+ ),
Where=co.DROPBOX_ID == None,
+ GroupBy=(ch.OWNER_UID, chm.QUOTA_USED_BYTES),
**kwds
)
- _orphanedLimited = _orphanedBase(True)
- _orphanedUnlimited = _orphanedBase(False)
- del _orphanedBase
+ _orphanedSummaryLimited = _orphanedSummary(True)
+ _orphanedSummaryUnlimited = _orphanedSummary(False)
+ del _orphanedSummary
-
def orphanedAttachments(self, batchSize=None):
"""
Find attachments no longer referenced by any events.
- Returns a deferred to a list of (dropbox_id, path) tuples.
+ Returns a deferred to a list of (calendar_home_owner_uid, dropbox_id, path, size) tuples.
"""
if batchSize is not None:
kwds = {'batchSize': batchSize}
- query = self._orphanedLimited
+ query = self._orphanedSummaryLimited
else:
kwds = {}
- query = self._orphanedUnlimited
+ query = self._orphanedSummaryUnlimited
return query.on(self, **kwds)
+ def _orphanedBase(limited): #@NoSelf
+ at = schema.ATTACHMENT
+ co = schema.CALENDAR_OBJECT
+ kwds = {}
+ if limited:
+ kwds["Limit"] = Parameter('batchSize')
+ return Select(
+ [at.DROPBOX_ID, at.PATH],
+ From=at.join(co, at.DROPBOX_ID == co.DROPBOX_ID, "left outer"),
+ Where=co.DROPBOX_ID == None,
+ **kwds
+ )
+
+ _orphanedLimited = _orphanedBase(True)
+ _orphanedUnlimited = _orphanedBase(False)
+ del _orphanedBase
+
+
@inlineCallbacks
def removeOrphanedAttachments(self, batchSize=None):
"""
@@ -1011,7 +1035,13 @@
# TODO: see if there is a better way to import Attachment
from txdav.caldav.datastore.sql import DropBoxAttachment
- results = (yield self.orphanedAttachments(batchSize=batchSize))
+ if batchSize is not None:
+ kwds = {'batchSize': batchSize}
+ query = self._orphanedLimited
+ else:
+ kwds = {}
+ query = self._orphanedUnlimited
+ results = (yield query.on(self, **kwds))
count = 0
for dropboxID, path in results:
attachment = (yield DropBoxAttachment.load(self, dropboxID, path))
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2012-12-12 22:11:41 UTC (rev 10162)
@@ -34,14 +34,6 @@
"MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
);
-create table INVITE (
- "INVITE_UID" nvarchar2(255),
- "NAME" nvarchar2(255),
- "RECIPIENT_ADDRESS" nvarchar2(255),
- "HOME_RESOURCE_ID" integer not null,
- "RESOURCE_ID" integer not null
-);
-
create table NOTIFICATION_HOME (
"RESOURCE_ID" integer primary key,
"OWNER_UID" nvarchar2(255) unique
@@ -65,8 +57,6 @@
"CALENDAR_RESOURCE_NAME" nvarchar2(255),
"BIND_MODE" integer not null,
"BIND_STATUS" integer not null,
- "SEEN_BY_OWNER" integer not null,
- "SEEN_BY_SHAREE" integer not null,
"MESSAGE" nclob,
primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
@@ -216,8 +206,6 @@
"ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
"BIND_MODE" integer not null,
"BIND_STATUS" integer not null,
- "SEEN_BY_OWNER" integer not null,
- "SEEN_BY_SHAREE" integer not null,
"MESSAGE" nclob,
primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_ID"),
unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
@@ -277,21 +265,10 @@
"VALUE" nvarchar2(255)
);
-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '13');
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '14');
insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '3');
insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '1');
-create index INVITE_INVITE_UID_9b0902ff on INVITE (
- INVITE_UID
-);
-create index INVITE_RESOURCE_ID_b36ddc23 on INVITE (
- RESOURCE_ID
-);
-
-create index INVITE_HOME_RESOURCE__e9bdf77e on INVITE (
- HOME_RESOURCE_ID
-);
-
create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
NOTIFICATION_HOME_RESOURCE_ID
);
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current.sql 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/current.sql 2012-12-12 22:11:41 UTC (rev 10162)
@@ -46,7 +46,7 @@
create table CALENDAR_HOME (
RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
OWNER_UID varchar(255) not null unique, -- implicit index
- DATAVERSION integer default 0 not null
+ DATAVERSION integer default 0 not null
);
----------------------------
@@ -81,7 +81,6 @@
);
-
---------------------------
-- Sharing Notifications --
---------------------------
@@ -279,6 +278,7 @@
create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
----------------
-- Attachment --
----------------
@@ -287,11 +287,11 @@
create table ATTACHMENT (
ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
- CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
DROPBOX_ID varchar(255),
- CONTENT_TYPE varchar(255) not null,
- SIZE integer not null,
- MD5 char(32) not null,
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
PATH varchar(1024) not null
@@ -332,7 +332,7 @@
create table ADDRESSBOOK_HOME (
RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
OWNER_UID varchar(255) not null unique, -- implicit index
- DATAVERSION integer default 0 not null
+ DATAVERSION integer default 0 not null
);
-------------------------------
Copied: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/oracle-dialect/v13.sql (from rev 10159, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v13.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/oracle-dialect/v13.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/oracle-dialect/v13.sql 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,379 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create table NODE_INFO (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table INVITE (
+ "INVITE_UID" nvarchar2(255),
+ "NAME" nvarchar2(255),
+ "RECIPIENT_ADDRESS" nvarchar2(255),
+ "HOME_RESOURCE_ID" integer not null,
+ "RESOURCE_ID" integer not null
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "SEEN_BY_OWNER" integer not null,
+ "SEEN_BY_SHAREE" integer not null,
+ "MESSAGE" nclob,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ORGANIZER_OBJECT" integer references CALENDAR_OBJECT,
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+ "INSTANCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ "FLOATING" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK (
+ "RESOURCE_ID" integer primary key
+);
+
+create table ADDRESSBOOK_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK on delete cascade,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK_BIND (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "SEEN_BY_OWNER" integer not null,
+ "SEEN_BY_SHAREE" integer not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_RESOURCE_ID", "VCARD_UID")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+ "ADDRESSBOOK_RESOURCE_ID" integer references ADDRESSBOOK,
+ "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '13');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '3');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '1');
+create index INVITE_INVITE_UID_9b0902ff on INVITE (
+ INVITE_UID
+);
+
+create index INVITE_RESOURCE_ID_b36ddc23 on INVITE (
+ RESOURCE_ID
+);
+
+create index INVITE_HOME_RESOURCE__e9bdf77e on INVITE (
+ HOME_RESOURCE_ID
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+ NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+ CALENDAR_RESOURCE_ID,
+ RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ORGAN_7ce24750 on CALENDAR_OBJECT (
+ ORGANIZER_OBJECT
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+ DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+ CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+ CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+ CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_BIND_RESO_205aa75c on ADDRESSBOOK_BIND (
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID,
+ CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_f460d62d on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID,
+ ADDRESSBOOK_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_9a848f39 on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_cb101e6b on ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_RESOURCE_ID,
+ REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID,
+ REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+ RESOURCE_KEY
+);
+
Copied: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/postgres-dialect/v13.sql (from rev 10159, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v13.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/postgres-dialect/v13.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/old/postgres-dialect/v13.sql 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,531 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2012 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+ HOSTNAME varchar(255) not null,
+ PID integer not null,
+ PORT integer not null,
+ TIME timestamp not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+ primary key (HOSTNAME, PORT)
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+ RESOURCE_ID integer primary key references CALENDAR on delete cascade, -- implicit index
+ SUPPORTED_COMPONENTS varchar(255) default null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+------------------------
+-- Sharing Invitation --
+------------------------
+
+create table INVITE (
+ INVITE_UID varchar(255) not null,
+ NAME varchar(255) not null,
+ RECIPIENT_ADDRESS varchar(255) not null,
+ HOME_RESOURCE_ID integer not null,
+ RESOURCE_ID integer not null
+
+ -- Need primary key on (INVITE_UID, NAME, RECIPIENT_ADDRESS)?
+);
+
+create index INVITE_INVITE_UID on INVITE(INVITE_UID);
+create index INVITE_RESOURCE_ID on INVITE(RESOURCE_ID);
+create index INVITE_HOME_RESOURCE_ID on INVITE(HOME_RESOURCE_ID);
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique -- implicit index
+);
+
+create table NOTIFICATION (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME,
+ NOTIFICATION_UID varchar(255) not null,
+ XML_TYPE varchar(255) not null,
+ XML_DATA text not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+ NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+
+ -- An invitation which hasn't been accepted yet will not yet have a resource
+ -- name, so this field may be null.
+
+ CALENDAR_RESOURCE_NAME varchar(255),
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ SEEN_BY_OWNER boolean not null,
+ SEEN_BY_SHAREE boolean not null,
+ MESSAGE text,
+
+ primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+ unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME) -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own' );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ ICALENDAR_TEXT text not null,
+ ICALENDAR_UID varchar(255) not null,
+ ICALENDAR_TYPE varchar(255) not null,
+ ATTACHMENTS_MODE integer default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+ DROPBOX_ID varchar(255),
+ ORGANIZER varchar(255),
+ ORGANIZER_OBJECT integer references CALENDAR_OBJECT,
+ RECURRANCE_MIN date, -- minimum date that recurrences have been expanded to.
+ RECURRANCE_MAX date, -- maximum date that recurrences have been expanded to.
+ ACCESS integer default 0 not null,
+ SCHEDULE_OBJECT boolean default false,
+ SCHEDULE_TAG varchar(36) default null,
+ SCHEDULE_ETAGS text default null,
+ PRIVATE_COMMENTS boolean default false not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+ -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+ -- calendar objects, this constraint has to be selectively enforced by the
+ -- application layer.
+
+ -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+ CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ORGANIZER_OBJECT on
+ CALENDAR_OBJECT(ORGANIZER_OBJECT);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+ CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, '' );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public' );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private' );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted' );
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+ INSTANCE_ID integer primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+ CALENDAR_RESOURCE_ID integer not null references CALENDAR on delete cascade,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ FLOATING boolean not null,
+ START_DATE timestamp not null,
+ END_DATE timestamp not null,
+ FBTYPE integer not null,
+ TRANSPARENT boolean not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+ TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+ ID integer primary key,
+ DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown' );
+insert into FREE_BUSY_TYPE values (1, 'free' );
+insert into FREE_BUSY_TYPE values (2, 'busy' );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative' );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+ TIME_RANGE_INSTANCE_ID integer not null references TIME_RANGE on delete cascade,
+ USER_ID varchar(255) not null,
+ TRANSPARENT boolean not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+ TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+ ATTACHMENT_ID integer primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ DROPBOX_ID varchar(255),
+ CONTENT_TYPE varchar(255) not null,
+ SIZE integer not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ PATH varchar(1024) not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+ ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+ ATTACHMENT_ID integer not null references ATTACHMENT on delete cascade,
+ MANAGED_ID varchar(255) not null,
+ CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+
+ primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+ unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+ RESOURCE_ID integer not null, -- foreign key: *.RESOURCE_ID
+ NAME varchar(255) not null,
+ VALUE text not null, -- FIXME: xml?
+ VIEWER_UID varchar(255),
+
+ primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ OWNER_UID varchar(255) not null unique, -- implicit index
+ DATAVERSION integer default 0 not null
+);
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+ QUOTA_USED_BYTES integer default 0 not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+-----------------
+-- AddressBook --
+-----------------
+
+create table ADDRESSBOOK (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+--------------------------
+-- AddressBook Metadata --
+--------------------------
+
+create table ADDRESSBOOK_METADATA (
+ RESOURCE_ID integer primary key references ADDRESSBOOK on delete cascade, -- implicit index
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+----------------------
+-- AddressBook Bind --
+----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK
+
+create table ADDRESSBOOK_BIND (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+
+ -- An invitation which hasn't been accepted yet will not yet have a resource
+ -- name, so this field may be null.
+
+ ADDRESSBOOK_RESOURCE_NAME varchar(255),
+ BIND_MODE integer not null, -- enum CALENDAR_BIND_MODE
+ BIND_STATUS integer not null, -- enum CALENDAR_BIND_STATUS
+ SEEN_BY_OWNER boolean not null,
+ SEEN_BY_SHAREE boolean not null,
+ MESSAGE text, -- FIXME: xml?
+
+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
+);
+
+create index ADDRESSBOOK_BIND_RESOURCE_ID on
+ ADDRESSBOOK_BIND(ADDRESSBOOK_RESOURCE_ID);
+
+create table ADDRESSBOOK_OBJECT (
+ RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+ ADDRESSBOOK_RESOURCE_ID integer not null references ADDRESSBOOK on delete cascade,
+ RESOURCE_NAME varchar(255) not null,
+ VCARD_TEXT text not null,
+ VCARD_UID varchar(255) not null,
+ MD5 char(32) not null,
+ CREATED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+ MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+ unique (ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+ unique (ADDRESSBOOK_RESOURCE_ID, VCARD_UID) -- implicit index
+);
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+---------------
+-- Revisions --
+---------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_HOME_RESOURCE_ID integer not null references CALENDAR_HOME,
+ CALENDAR_RESOURCE_ID integer references CALENDAR,
+ CALENDAR_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+-------------------------------
+-- AddressBook Object Revisions --
+-------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+ ADDRESSBOOK_HOME_RESOURCE_ID integer not null references ADDRESSBOOK_HOME,
+ ADDRESSBOOK_RESOURCE_ID integer references ADDRESSBOOK,
+ ADDRESSBOOK_NAME varchar(255) default null,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_ADDRESSBOOK_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, REVISION);
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ NOTIFICATION_HOME_RESOURCE_ID integer not null references NOTIFICATION_HOME on delete cascade,
+ RESOURCE_NAME varchar(255),
+ REVISION integer default nextval('REVISION_SEQ') not null,
+ DELETED boolean not null,
+
+ unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+ on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+ TOKEN varchar(255) not null,
+ RESOURCE_KEY varchar(255) not null,
+ MODIFIED integer not null,
+ SUBSCRIBER_GUID varchar(255) not null,
+ USER_AGENT varchar(255) default null,
+ IP_ADDR varchar(255) default null,
+
+ primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+ on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+ NAME varchar(255) primary key, -- implicit index
+ VALUE varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '13');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '3');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '1');
Copied: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql (from rev 10159, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_13_to_14.sql 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,33 @@
+----
+-- Copyright (c) 2012 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 13 to 14 --
+---------------------------------------------------
+
+-- sharing-related cleanup
+
+drop table INVITE;
+
+alter table CALENDAR_BIND
+ drop column SEEN_BY_OWNER, SEEN_BY_SHAREE;
+
+alter table ADDRESSBOOK_BIND
+ drop column SEEN_BY_OWNER, SEEN_BY_SHAREE;
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '14' where NAME = 'VERSION';
Copied: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql (from rev 10159, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql)
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql (rev 0)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql 2012-12-12 22:11:41 UTC (rev 10162)
@@ -0,0 +1,37 @@
+----
+-- Copyright (c) 2012 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 13 to 14 --
+---------------------------------------------------
+
+-- sharing-related cleanup
+
+drop table INVITE;
+
+alter table CALENDAR_BIND
+ drop column SEEN_BY_OWNER;
+alter table CALENDAR_BIND
+ drop column SEEN_BY_SHAREE;
+
+alter table ADDRESSBOOK_BIND
+ drop column SEEN_BY_OWNER;
+alter table ADDRESSBOOK_BIND
+ drop column SEEN_BY_SHAREE;
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '14' where NAME = 'VERSION';
Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_tables.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_tables.py 2012-12-12 21:59:33 UTC (rev 10161)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/common/datastore/sql_tables.py 2012-12-12 22:11:41 UTC (rev 10162)
@@ -114,8 +114,8 @@
"""
result = {}
result['name'] = tableSyntax.model.name
- #pkey = tableSyntax.model.primaryKey
- #if pkey is not None:
+ # pkey = tableSyntax.model.primaryKey
+ # if pkey is not None:
# default = pkey.default
# if isinstance(default, Sequence):
# result['sequence'] = default.name
@@ -399,11 +399,8 @@
if __name__ == '__main__':
import sys
if len(sys.argv) == 2:
- # Argument is the name of a old/postgres-dialect file (without the .sql suffix), e.g. "v4"
+ # Argument is the name of a old/postgres-dialect file (without the .sql suffix), e.g. "v4"
schema = _populateSchema(sys.argv[1])
else:
schema = _populateSchema()
_translateSchema(sys.stdout, schema=schema)
-
-
-
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121212/d2c9f558/attachment-0001.html>
More information about the calendarserver-changes
mailing list