use alembic to initialize the backend database
[pithos] / snf-pithos-backend / pithos / backends / lib / sqlalchemy / xfeatures.py
index 8fca110..9604416 100644 (file)
@@ -36,9 +36,31 @@ from sqlalchemy import Table, Column, String, Integer, MetaData, ForeignKey
 from sqlalchemy.sql import select, and_
 from sqlalchemy.schema import Index
 from sqlalchemy.sql.expression import desc
+from sqlalchemy.exc import NoSuchTableError
 
 from dbworker import DBWorker
 
+def create_tables(engine):
+    metadata = MetaData()
+    columns=[]
+    columns.append(Column('feature_id', Integer, primary_key=True))
+    columns.append(Column('path', String(2048)))
+    xfeatures = Table('xfeatures', metadata, *columns, mysql_engine='InnoDB')
+    # place an index on path
+    Index('idx_features_path', xfeatures.c.path, unique=True)
+    
+    columns=[]
+    columns.append(Column('feature_id', Integer,
+                          ForeignKey('xfeatures.feature_id',
+                                     ondelete='CASCADE'),
+                          primary_key=True))
+    columns.append(Column('key', Integer, primary_key=True,
+                          autoincrement=False))
+    columns.append(Column('value', String(256), primary_key=True))
+    xfeaturevals = Table('xfeaturevals', metadata, *columns, mysql_engine='InnoDB')
+    
+    metadata.create_all(engine)
+    return metadata.sorted_tables
 
 class XFeatures(DBWorker):
     """XFeatures are path properties that allow non-nested
@@ -47,73 +69,47 @@ class XFeatures(DBWorker):
     
     def __init__(self, **params):
         DBWorker.__init__(self, **params)
-        metadata = MetaData()
-        columns=[]
-        columns.append(Column('feature_id', Integer, primary_key=True))
-        columns.append(Column('path', String(2048)))
-        self.xfeatures = Table('xfeatures', metadata, *columns, mysql_engine='InnoDB')
-        # place an index on path
-        Index('idx_features_path', self.xfeatures.c.path, unique=True)
-        
-        columns=[]
-        columns.append(Column('feature_id', Integer,
-                              ForeignKey('xfeatures.feature_id',
-                                         ondelete='CASCADE'),
-                              primary_key=True))
-        columns.append(Column('key', Integer, primary_key=True,
-                              autoincrement=False))
-        columns.append(Column('value', String(255), primary_key=True))
-        self.xfeaturevals = Table('xfeaturevals', metadata, *columns, mysql_engine='InnoDB')
-        
-        metadata.create_all(self.engine)
+        try:
+            metadata = MetaData(self.engine)
+            self.xfeatures = Table('xfeatures', metadata, autoload=True)
+            self.xfeaturevals = Table('xfeaturevals', metadata, autoload=True)
+        except NoSuchTableError:
+            tables = create_tables(self.engine)
+            map(lambda t: self.__setattr__(t.name, t), tables)
     
-    def xfeature_inherit(self, path):
-        """Return the (path, feature) inherited by the path, or None."""
-        
-        s = select([self.xfeatures.c.path, self.xfeatures.c.feature_id])
-        s = s.where(self.xfeatures.c.path <= path)
-        s = s.order_by(desc(self.xfeatures.c.path)).limit(1)
-        r = self.conn.execute(s)
-        row = r.fetchone()
-        r.close()
-        if row and path.startswith(row[0]):
-            return row
-        else:
-            return None
+#     def xfeature_inherit(self, path):
+#         """Return the (path, feature) inherited by the path, or None."""
+#         
+#         s = select([self.xfeatures.c.path, self.xfeatures.c.feature_id])
+#         s = s.where(self.xfeatures.c.path <= path)
+#         #s = s.where(self.xfeatures.c.path.like(self.escape_like(path) + '%', escape='\\')) # XXX: Implement reverse and escape like...
+#         s = s.order_by(desc(self.xfeatures.c.path))
+#         r = self.conn.execute(s)
+#         l = r.fetchall()
+#         r.close()
+#         return l
     
-    def xfeature_list(self, path):
-        """Return the list of the (prefix, feature) pairs matching path.
-           A prefix matches path if either the prefix includes the path,
-           or the path includes the prefix.
-        """
-        
-        inherited = self.xfeature_inherit(path)
-        if inherited:
-            return [inherited]
+    def xfeature_get(self, path):
+        """Return feature for path."""
         
-        s = select([self.xfeatures.c.path, self.xfeatures.c.feature_id])
-        s = s.where(and_(self.xfeatures.c.path.like(self.escape_like(path) + '%', escape='\\'),
-                     self.xfeatures.c.path != path))
+        s = select([self.xfeatures.c.feature_id])
+        s = s.where(self.xfeatures.c.path == path)
         s = s.order_by(self.xfeatures.c.path)
         r = self.conn.execute(s)
-        l = r.fetchall()
+        row = r.fetchone()
         r.close()
-        return l
+        if row:
+            return row[0]
+        return None
     
     def xfeature_create(self, path):
         """Create and return a feature for path.
-           If the path already inherits a feature or
-           bestows to paths already inheriting a feature,
-           create no feature and return None.
            If the path has a feature, return it.
         """
         
-        prefixes = self.xfeature_list(path)
-        pl = len(prefixes)
-        if (pl > 1) or (pl == 1 and prefixes[0][0] != path):
-            return None
-        if pl == 1 and prefixes[0][0] == path:
-            return prefixes[0][1]
+        feature = self.xfeature_get(path)
+        if feature is not None:
+            return feature
         s = self.xfeatures.insert()
         r = self.conn.execute(s, path=path)
         inserted_primary_key = r.inserted_primary_key[0]
@@ -127,6 +123,13 @@ class XFeatures(DBWorker):
         r = self.conn.execute(s)
         r.close()
     
+    def xfeature_destroy_bulk(self, paths):
+        """Destroy features and all their key, value pairs."""
+        
+        s = self.xfeatures.delete().where(self.xfeatures.c.path.in_(paths))
+        r = self.conn.execute(s)
+        r.close()
+    
     def feature_dict(self, feature):
         """Return a dict mapping keys to list of values for feature."""