In version_remove recompute nodes latest version
[pithos] / snf-pithos-backend / pithos / backends / lib / sqlite / node.py
index 784296f..0c69a31 100644 (file)
@@ -115,12 +115,15 @@ class Node(DBWorker):
                           ( node       integer primary key,
                             parent     integer default 0,
                             path       text    not null default '',
+                            latest_version     integer,
                             foreign key (parent)
                             references nodes(node)
                             on update cascade
                             on delete cascade ) """)
         execute(""" create unique index if not exists idx_nodes_path
                     on nodes(path) """)
+        execute(""" create index if not exists idx_nodes_parent
+                    on nodes(parent) """)
         
         execute(""" create table if not exists policy
                           ( node   integer,
@@ -207,9 +210,12 @@ class Node(DBWorker):
         """
         
         placeholders = ','.join('?' for path in paths)
-        q = "select path, node from nodes where path in (%s)" % placeholders
+        q = "select node from nodes where path in (%s)" % placeholders
         self.execute(q, paths)
-        return self.fetchall()
+        r = self.fetchall()
+        if r is not None:
+               return [row[0] for row in r]
+        return None
     
     def node_get_properties(self, node):
         """Return the node's (parent, path).
@@ -430,12 +436,11 @@ class Node(DBWorker):
         
         # The latest version.
         q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
-             "from versions "
-             "where serial = (select max(serial) "
-                             "from versions "
-                             "where node = ? and mtime < ?) "
+             "from versions v "
+             "where serial = %s "
              "and cluster != ?")
-        execute(q, (node, before, except_cluster))
+        subq, args = self._construct_latest_version_subquery(node=node, before=before)
+        execute(q % subq, args + [except_cluster])
         props = fetchone()
         if props is None:
             return None
@@ -444,14 +449,13 @@ class Node(DBWorker):
         # First level, just under node (get population).
         q = ("select count(serial), sum(size), max(mtime) "
              "from versions v "
-             "where serial = (select max(serial) "
-                             "from versions "
-                             "where node = v.node and mtime < ?) "
+             "where serial = %s "
              "and cluster != ? "
              "and node in (select node "
                           "from nodes "
                           "where parent = ?)")
-        execute(q, (before, except_cluster, node))
+        subq, args = self._construct_latest_version_subquery(node=None, before=before)
+        execute(q % subq, args + [except_cluster, node])
         r = fetchone()
         if r is None:
             return None
@@ -464,14 +468,13 @@ class Node(DBWorker):
         # This is why the full path is stored.
         q = ("select count(serial), sum(size), max(mtime) "
              "from versions v "
-             "where serial = (select max(serial) "
-                             "from versions "
-                             "where node = v.node and mtime < ?) "
+             "where serial = %s "
              "and cluster != ? "
              "and node in (select node "
                           "from nodes "
                           "where path like ? escape '\\')")
-        execute(q, (before, except_cluster, self.escape_like(path) + '%'))
+        subq, args = self._construct_latest_version_subquery(node=None, before=before)
+        execute(q % subq, args + [except_cluster, self.escape_like(path) + '%'])
         r = fetchone()
         if r is None:
             return None
@@ -479,6 +482,16 @@ class Node(DBWorker):
         mtime = max(mtime, r[2])
         return (count, size, mtime)
     
+    def nodes_set_latest_version(self, node, serial=None):
+       if not serial:
+               q = ("select serial from versions where node = ? order_by mtime desc limit 1")
+               self.execute(q, (node,))
+               r = self.fetchone()
+                       serial = r[0] of r else None
+       q = ("update nodes set latest_version = ? where node = ?")
+        props = (serial, node)
+        self.execute(q, props)
+    
     def version_create(self, node, hash, size, type, source, muser, uuid, checksum, cluster=0):
         """Create a new version from the given properties.
            Return the (serial, mtime) of the new version.
@@ -490,42 +503,53 @@ class Node(DBWorker):
         props = (node, hash, size, type, source, mtime, muser, uuid, checksum, cluster)
         serial = self.execute(q, props).lastrowid
         self.statistics_update_ancestors(node, 1, size, mtime, cluster)
+        
+        self.nodes_set_latest_version(node, serial)
+        
         return serial, mtime
     
-    def version_lookup(self, node, before=inf, cluster=0):
+    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
         """Lookup the current version of the given node.
            Return a list with its properties:
            (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster)
            or None if the current version is not found in the given cluster.
         """
         
-        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
-             "from versions "
-             "where serial = (select max(serial) "
-                             "from versions "
-                             "where node = ? and mtime < ?) "
+        q = ("select %s "
+             "from versions v "
+             "where serial = %s "
              "and cluster = ?")
-        self.execute(q, (node, before, cluster))
+        subq, args = self._construct_latest_version_subquery(node=node, before=before)
+        if not all_props:
+            q = q % ("serial", subq)
+        else:
+            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster", subq)
+        
+        self.execute(q, args + [cluster])
         props = self.fetchone()
         if props is not None:
             return props
         return None
 
-    def version_lookup_bulk(self, nodes, before=inf, cluster=0):
+    def version_lookup_bulk(self, nodes, before=inf, cluster=0, all_props=True):
         """Lookup the current versions of the given nodes.
            Return a list with their properties:
            (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
         """
         
-        placeholders = ','.join('?' for node in nodes)
-        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
+        if not nodes:
+               return ()
+        q = ("select %s "
              "from versions "
-             "where serial in (select max(serial) "
-                             "from versions "
-                             "where node in (%s) and mtime < ? group by node) "
-             "and cluster = ?" % placeholders)
-        args = nodes
-        args.extend((before, cluster))
+             "where serial in %s "
+             "and cluster = ? %s")
+        subq, args = self._construct_latest_versions_subquery(nodes=nodes, before = before)
+        if not all_props:
+            q = q % ("serial", subq, '')
+        else:
+            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster",  subq, 'order by node')
+        
+        args += [cluster]
         self.execute(q, args)
         return self.fetchall()
     
@@ -591,6 +615,9 @@ class Node(DBWorker):
         
         q = "delete from versions where serial = ?"
         self.execute(q, (serial,))
+        
+        self.nodes_set_latest_version(node)
+        
         return hash, size
     
     def attribute_get(self, serial, domain, keys=()):
@@ -712,6 +739,53 @@ class Node(DBWorker):
         
         return subq, args
     
+    def _construct_versions_nodes_latest_version_subquery(self, before=inf):
+        if before == inf:
+            q = ("n.latest_version ")
+            args = []
+        else:
+            q = ("(select max(serial) "
+                                  "from versions "
+                                  "where node = v.node and mtime < ?) ")
+            args = [before]
+        return q, args
+    
+    def _construct_latest_version_subquery(self, node=None, before=inf):
+        where_cond = "node = v.node"
+        args = []
+        if node:
+            where_cond = "node = ? "
+            args = [node]
+        
+        if before == inf:
+            q = ("(select latest_version "
+                   "from nodes "
+                   "where %s) ")
+        else:
+            q = ("(select max(serial) "
+                   "from versions "
+                   "where %s and mtime < ?) ")
+            args += [before]
+        return q % where_cond, args
+    
+    def _construct_latest_versions_subquery(self, nodes=(), before=inf):
+        where_cond = ""
+        args = []
+        if nodes:
+            where_cond = "node in (%s) " % ','.join('?' for node in nodes)
+            args = nodes
+        
+        if before == inf:
+            q = ("(select latest_version "
+                   "from nodes "
+                   "where %s ) ")
+        else:
+            q = ("(select max(serial) "
+                                "from versions "
+                                "where %s and mtime < ? group by node) ")
+            args += [before]
+        return q % where_cond, args
+    
     def latest_attribute_keys(self, parent, domain, before=inf, except_cluster=0, pathq=[]):
         """Return a list with all keys pairs defined
            for all latest versions under parent that
@@ -721,9 +795,7 @@ class Node(DBWorker):
         # TODO: Use another table to store before=inf results.
         q = ("select distinct a.key "
              "from attributes a, versions v, nodes n "
-             "where v.serial = (select max(serial) "
-                               "from versions "
-                               "where node = v.node and mtime < ?) "
+             "where v.serial = %s "
              "and v.cluster != ? "
              "and v.node in (select node "
                             "from nodes "
@@ -731,7 +803,9 @@ class Node(DBWorker):
              "and a.serial = v.serial "
              "and a.domain = ? "
              "and n.node = v.node")
-        args = (before, except_cluster, parent, domain)
+        subq, subargs = self._construct_latest_version_subquery(node=None, before=before)
+        args = subargs + [except_cluster, parent, domain]
+        q = q % subq
         subq, subargs = self._construct_paths(pathq)
         if subq is not None:
             q += subq
@@ -801,20 +875,20 @@ class Node(DBWorker):
         
         q = ("select distinct n.path, %s "
              "from versions v, nodes n "
-             "where v.serial = (select max(serial) "
-                               "from versions "
-                               "where node = v.node and mtime < ?) "
+             "where v.serial = %s "
              "and v.cluster != ? "
              "and v.node in (select node "
                             "from nodes "
                             "where parent = ?) "
              "and n.node = v.node "
              "and n.path > ? and n.path < ?")
+        subq, args = self._construct_versions_nodes_latest_version_subquery(before)
         if not all_props:
-            q = q % "v.serial"
+            q = q % ("v.serial", subq)
         else:
-            q = q % "v.serial, v.node, v.hash, v.size, v.type, v.source, v.mtime, v.muser, v.uuid, v.checksum, v.cluster"
-        args = [before, except_cluster, parent, start, nextling]
+            q = q % ("v.serial, v.node, v.hash, v.size, v.type, v.source, v.mtime, v.muser, v.uuid, v.checksum, v.cluster", subq)
+        args += [except_cluster, parent, start, nextling]
+        start_index = len(args) - 2
         
         subq, subargs = self._construct_paths(pathq)
         if subq is not None:
@@ -873,7 +947,7 @@ class Node(DBWorker):
             if count >= limit: 
                 break
             
-            args[3] = strnextling(pf) # New start.
+            args[start_index] = strnextling(pf) # New start.
             execute(q, args)
         
         return matches, prefixes