[CalendarServer-changes] [2591] CalendarServer/branches/users/cdaboo/sqlpropstore-2563/twistedcaldav /sqlprops.py

source_changes at macosforge.org source_changes at macosforge.org
Wed Jun 18 09:38:12 PDT 2008


Revision: 2591
          http://trac.macosforge.org/projects/calendarserver/changeset/2591
Author:   cdaboo at apple.com
Date:     2008-06-18 09:38:11 -0700 (Wed, 18 Jun 2008)

Log Message:
-----------
Don't pickle the property objects. Just use the xml representation in the DB and parse the XML when
read out of the DB (though we cache the actual property objects).

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/sqlpropstore-2563/twistedcaldav/sqlprops.py

Modified: CalendarServer/branches/users/cdaboo/sqlpropstore-2563/twistedcaldav/sqlprops.py
===================================================================
--- CalendarServer/branches/users/cdaboo/sqlpropstore-2563/twistedcaldav/sqlprops.py	2008-06-18 16:36:24 UTC (rev 2590)
+++ CalendarServer/branches/users/cdaboo/sqlpropstore-2563/twistedcaldav/sqlprops.py	2008-06-18 16:38:11 UTC (rev 2591)
@@ -26,10 +26,10 @@
 
 __all__ = ["sqlPropertyStore"]
 
-import cPickle
 import os
 
 from twisted.web2 import responsecode
+from twisted.web2.dav import davxml
 from twisted.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav.log import Logger
@@ -74,7 +74,7 @@
                 "No such property: {%s}%s" % qname
             ))
             
-        value = self.index.getOnePropertyValue(self.rname, qname)
+        value = self.index.getOnePropertyForResource(self.rname, qname)
         if not value:
             raise HTTPError(StatusResponse(
                 responsecode.NOT_FOUND,
@@ -96,7 +96,7 @@
                 "No properties"
             ))
             
-        return self.index.getAllPropertyValues(self.rname, hidden)
+        return self.index.getAllPropertiesForResource(self.rname, hidden)
 
     def set(self, property):
         """
@@ -106,7 +106,7 @@
         """
 
         if self.index:
-            self.index.setOnePropertyValue(self.rname, property.qname(), property, property.hidden)
+            self.index.setOnePropertyForResource(self.rname, property)
 
     def _setSeveral(self, properties):
         """
@@ -116,7 +116,7 @@
         """
 
         if self.index:
-            self.index.setSeveralPropertyValues(self.rname, [(p.qname(), p, p.hidden) for p in properties])
+            self.index.setSeveralPropertiesForResource(self.rname, properties)
 
     def _setAll(self, properties):
         """
@@ -126,8 +126,8 @@
         """
 
         if self.index:
-            self.index.removeAllProperties(self.rname)
-            self.index.setSeveralPropertyValues(self.rname, [(p.qname(), p, p.hidden) for p in properties])
+            self.index.removeAllPropertiesForResource(self.rname)
+            self.index.setSeveralPropertiesForResource(self.rname, properties)
 
     def delete(self, qname):
         """
@@ -139,7 +139,7 @@
         """
         
         if self.index:
-            self.index.removeProperty(self.rname, qname)
+            self.index.removeOnePropertyForResource(self.rname, qname)
 
     def deleteAll(self):
         """
@@ -149,11 +149,11 @@
         """
         
         if self.index:
-            self.index.removeAllProperties(self.rname)
+            self.index.removeAllPropertiesForResource(self.rname)
 
     def contains(self, qname):
         if self.index:
-            value = self.index.getOnePropertyValue(self.rname, qname)
+            value = self.index.getOnePropertyForResource(self.rname, qname)
             return value is not None
         else:
             return False
@@ -167,7 +167,7 @@
         """
 
         if self.index:
-            return self.index.listProperties(self.rname)
+            return self.index.listPropertiesForResource(self.rname)
         else:
             return ()
 
@@ -205,7 +205,7 @@
     
     Properties Database:
     
-    ROW: RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT, PROPERTYVALUE
+    ROW: RESOURCENAME, PROPERTYNAME, PROPERTYXML
     
     """
     
@@ -214,11 +214,11 @@
     dbFormatVersion = "1"
 
     @classmethod
-    def _encode(cls, name):
+    def _encodePropertyName(cls, name):
         return "{%s}%s" % name
 
     @classmethod
-    def _decode(cls, name):
+    def _decodePropertyName(cls, name):
         index = name.find("}")
     
         if (index is -1 or not len(name) > index or not name[0] == "{"):
@@ -226,6 +226,29 @@
     
         return (name[1:index], name[index+1:])
 
+    @classmethod
+    def _decodePropertyValue(cls, pname, pxml):
+        """
+        @param pname: name of the property
+        @type pname: str
+        @param pxml: a property encoded as XML
+        @type pxml: str
+        @return: the davxml Element of the property, or C{None} if pxml is empty or C{None}
+        """
+        
+        result = None
+        if pxml:
+            try:
+                doc = davxml.WebDAVDocument.fromString(pxml)
+                result = doc.root_element
+            except ValueError:
+                msg = ("Invalid property value stored on server: {%s}%s %s"
+                       % (pname[0], pname[1], pxml))
+                log.err(msg)
+                raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, msg))
+
+        return result
+
     def __init__(self, path, use_cache=True):
         path = os.path.join(path, SQLPropertiesDatabase.dbFilename)
         super(SQLPropertiesDatabase, self).__init__(path, True, utf8=True)
@@ -233,7 +256,7 @@
         self.use_cache = use_cache
         self.cache = {}
 
-    def getOnePropertyValue(self, rname, pname):
+    def getOnePropertyForResource(self, rname, pname):
         """
         Get a property.
     
@@ -247,18 +270,19 @@
             # Make sure we have a cache entry
             if not self.cache.has_key(rname):
                 self.cache[rname] = {}
-                for row in self._db_execute("select PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1", rname):
-                    self.cache[rname][self._decode(row[0])] = cPickle.loads(row[1])
+                for row in self._db_execute("select PROPERTYNAME, PROPERTYXML from PROPERTIES where RESOURCENAME = :1", rname):
+                    sqlpname = self._decodePropertyName(row[0])
+                    sqlpvalue = self._decodePropertyValue(sqlpname, row[1])
+                    self.cache[rname][sqlpname] = sqlpvalue
             
             # Get the property and do negative caching if not present
             return self.cache[rname].setdefault(pname, None)
         else:
-            property = self._db_value_for_sql("select PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1 and PROPERTYNAME = :2", rname, self._encode(pname))
-            if property:
-                property = cPickle.loads(property)
-            return property
+            pxml = self._db_value_for_sql("select PROPERTYXML from PROPERTIES where RESOURCENAME = :1 and PROPERTYNAME = :2", rname, self._encodePropertyName(pname))
+            pvalue = self._decodePropertyValue(pname, pxml)
+            return pvalue
 
-    def getAllPropertyValues(self, rname, hidden):
+    def getAllPropertiesForResource(self, rname, hidden):
         """
         Get specified property values from specific resource.
     
@@ -268,15 +292,17 @@
         """
         
         properties = {}
-        statement = "select PROPERTYNAME, PROPERTYOBJECT from PROPERTIES where RESOURCENAME = :1"
+        statement = "select PROPERTYNAME, PROPERTYXML from PROPERTIES where RESOURCENAME = :1"
         if not hidden:
             statement += " and HIDDEN = 'F'"
         for row in self._db_execute(statement, rname):
-            properties[self._decode(row[0])] = cPickle.loads(row[1])
+            pname = self._decodePropertyName(row[0])
+            pvalue = self._decodePropertyValue(pname, row[1])
+            properties[pname] = pvalue
 
         return properties
 
-    def setOnePropertyValue(self, rname, pname, pvalue, hidden):
+    def setOnePropertyForResource(self, rname, property):
         """
         Add a property.
     
@@ -286,15 +312,14 @@
         @param hidden: C{True} for a hidden property, C{False} otherwise.
         """
         
-        # Remove what is there, then add it back.
-        self._delete_from_db(rname, self._encode(pname))
-        self._add_to_db(rname, self._encode(pname), cPickle.dumps(pvalue), pvalue.toxml(), hidden)
+        pname = property.qname()
+        self._add_to_db(rname, self._encodePropertyName(pname), property.toxml(), property.hidden)
         self._db_commit()
         
         if self.use_cache and self.cache.has_key(rname):
-            self.cache[rname][pname] = pvalue 
+            self.cache[rname][pname] = property 
 
-    def setSeveralPropertyValues(self, rname, properties):
+    def setSeveralPropertiesForResource(self, rname, properties):
         """
         Add a set of properties.
     
@@ -303,16 +328,15 @@
         """
         
         # Remove what is there, then add it back.
-        for p in properties:
-            self._delete_from_db(rname, self._encode(p[0]))
-            self._add_to_db(rname, self._encode(p[0]), cPickle.dumps(p[1]), p[1].toxml(), p[2])
+        for property in properties:
+            self._add_to_db(rname, self._encodePropertyName(property.qname()), property.toxml(), property.hidden)
         self._db_commit()
 
         if self.use_cache and self.cache.has_key(rname):
-            for p in properties:
-                self.cache[rname][p[0]] = p[1]
+            for property in properties:
+                self.cache[rname][property.qname()] = property
 
-    def removeProperty(self, rname, pname):
+    def removeOnePropertyForResource(self, rname, pname):
         """
         Remove a property.
     
@@ -320,13 +344,13 @@
         @param pname: a C{str} containing the name of the property to remove.
         """
 
-        self._delete_from_db(rname, self._encode(pname))
+        self._delete_from_db(rname, self._encodePropertyName(pname))
         self._db_commit()
         
         if self.use_cache and self.cache.has_key(rname):
             self.cache[rname][pname] = None
 
-    def removeAllProperties(self, rname):
+    def removeAllPropertiesForResource(self, rname):
         """
         Remove all properties for resource.
     
@@ -342,7 +366,7 @@
             except KeyError:
                 pass
 
-    def listProperties(self, rname):
+    def listPropertiesForResource(self, rname):
         """
         List all properties in resource.
     
@@ -355,24 +379,24 @@
             members.update(self.cache[rname].iterkeys())
         else:
             for row in self._db_execute("select PROPERTYNAME from PROPERTIES where RESOURCENAME = :1", rname):
-                members.add(self._decode(row[0]))
+                members.add(self._decodePropertyName(row[0]))
         return members
 
-    def _add_to_db(self, rname, pname, pobject, pvalue, hidden):
+    def _add_to_db(self, rname, pname, pxml, hidden):
         """
         Add a property.
     
         @param rname: a C{str} containing the resource name.
         @param pname: a C{str} containing the name of the property to set.
-        @param pobject: a C{str} containing the pickled representation of the property object.
-        @param pvalue: a C{str} containing the text of the property value to set.
+        @param pxml: a C{str} containing the XML representation of the property object.
+        @param hidden: a C{bool} indicating whether the property is hidden.
         """
         
         self._db_execute(
             """
-            insert into PROPERTIES (RESOURCENAME, PROPERTYNAME, PROPERTYOBJECT, PROPERTYVALUE, HIDDEN)
-            values (:1, :2, :3, :4, :5)
-            """, rname, pname, pobject, pvalue, "T" if hidden else "F"
+            insert or replace into PROPERTIES (RESOURCENAME, PROPERTYNAME, PROPERTYXML, HIDDEN)
+            values (:1, :2, :3, :4)
+            """, rname, pname, pxml, "T" if hidden else "F"
         )
        
     def _delete_from_db(self, rname, pname):
@@ -381,7 +405,6 @@
     
         @param rname: a C{str} containing the resource name.
         @param pname: a C{str} containing the name of the property to get.
-        @return: a C{str} containing the property value.
         """
 
         self._db_execute("delete from PROPERTIES where RESOURCENAME = :1 and PROPERTYNAME = :2", rname, pname)
@@ -392,7 +415,6 @@
     
         @param rname: a C{str} containing the resource name.
         @param pname: a C{str} containing the name of the property to get.
-        @return: a C{str} containing the property value.
         """
 
         self._db_execute("delete from PROPERTIES where RESOURCENAME = :1", rname)
@@ -411,31 +433,31 @@
 
     def _db_init_data_tables(self, q):
         """
-        Initialise the underlying database tables.
+        Initialize the underlying database tables.
         @param q:           a database cursor to use.
         """
 
         #
         # PROPERTIES table
         #
+        # (RESOURCENAME, PROPERTYNAME) is unique and we default to replacing whatever is
+        # already present for those pairs. Using UNIQUE on those creates an index for us.
+        #
         q.execute(
             """
             create table PROPERTIES (
                 RESOURCENAME   text,
                 PROPERTYNAME   text,
-                PROPERTYOBJECT text,
-                PROPERTYVALUE  text,
-                HIDDEN         text(1)
+                PROPERTYXML    text,
+                HIDDEN         text(1),
+                UNIQUE         (RESOURCENAME, PROPERTYNAME) ON CONFLICT REPLACE
             )
             """
         )
+        
+        # When deleting a resource as a whole we search on the RESOURCENAME so index that column.
         q.execute(
             """
             create index RESOURCE on PROPERTIES (RESOURCENAME)
             """
         )
-        q.execute(
-            """
-            create index RESOURCEandPROPERTY on PROPERTIES (RESOURCENAME, PROPERTYNAME)
-            """
-        )

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080618/88a5f60f/attachment-0001.htm 


More information about the calendarserver-changes mailing list