Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / lib / sqlite / node.py @ a5b0519c

History | View | Annotate | Download (36.8 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 2715ade4 Sofia Papagiannaki
#
3 1f2d1b42 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 1f2d1b42 Antony Chazapis
# without modification, are permitted provided that the following
5 1f2d1b42 Antony Chazapis
# conditions are met:
6 2715ade4 Sofia Papagiannaki
#
7 1f2d1b42 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 1f2d1b42 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 1f2d1b42 Antony Chazapis
#      disclaimer.
10 2715ade4 Sofia Papagiannaki
#
11 1f2d1b42 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 1f2d1b42 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 1f2d1b42 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 1f2d1b42 Antony Chazapis
#      provided with the distribution.
15 2715ade4 Sofia Papagiannaki
#
16 1f2d1b42 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 1f2d1b42 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 1f2d1b42 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1f2d1b42 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 1f2d1b42 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 1f2d1b42 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 1f2d1b42 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 1f2d1b42 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 1f2d1b42 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 1f2d1b42 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 1f2d1b42 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 1f2d1b42 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 2715ade4 Sofia Papagiannaki
#
29 1f2d1b42 Antony Chazapis
# The views and conclusions contained in the software and
30 1f2d1b42 Antony Chazapis
# documentation are those of the authors and should not be
31 1f2d1b42 Antony Chazapis
# interpreted as representing official policies, either expressed
32 1f2d1b42 Antony Chazapis
# or implied, of GRNET S.A.
33 1f2d1b42 Antony Chazapis
34 1f2d1b42 Antony Chazapis
from time import time
35 5576e6dd Sofia Papagiannaki
from operator import itemgetter
36 5576e6dd Sofia Papagiannaki
from itertools import groupby
37 1f2d1b42 Antony Chazapis
38 1f2d1b42 Antony Chazapis
from dbworker import DBWorker
39 1f2d1b42 Antony Chazapis
40 6e147ecc Antony Chazapis
from pithos.backends.filter import parse_filters
41 1713c946 Antony Chazapis
42 1f2d1b42 Antony Chazapis
43 2715ade4 Sofia Papagiannaki
ROOTNODE = 0
44 1f2d1b42 Antony Chazapis
45 2715ade4 Sofia Papagiannaki
(SERIAL, NODE, HASH, SIZE, TYPE, SOURCE, MTIME, MUSER, UUID, CHECKSUM,
46 2715ade4 Sofia Papagiannaki
 CLUSTER) = range(11)
47 1f2d1b42 Antony Chazapis
48 2715ade4 Sofia Papagiannaki
(MATCH_PREFIX, MATCH_EXACT) = range(2)
49 1f2d1b42 Antony Chazapis
50 1f2d1b42 Antony Chazapis
inf = float('inf')
51 1f2d1b42 Antony Chazapis
52 1f2d1b42 Antony Chazapis
53 1f2d1b42 Antony Chazapis
def strnextling(prefix):
54 60b8a083 Antony Chazapis
    """Return the first unicode string
55 1f2d1b42 Antony Chazapis
       greater than but not starting with given prefix.
56 1f2d1b42 Antony Chazapis
       strnextling('hello') -> 'hellp'
57 1f2d1b42 Antony Chazapis
    """
58 1f2d1b42 Antony Chazapis
    if not prefix:
59 1f2d1b42 Antony Chazapis
        ## all strings start with the null string,
60 1f2d1b42 Antony Chazapis
        ## therefore we have to approximate strnextling('')
61 1f2d1b42 Antony Chazapis
        ## with the last unicode character supported by python
62 1f2d1b42 Antony Chazapis
        ## 0x10ffff for wide (32-bit unicode) python builds
63 1f2d1b42 Antony Chazapis
        ## 0x00ffff for narrow (16-bit unicode) python builds
64 1f2d1b42 Antony Chazapis
        ## We will not autodetect. 0xffff is safe enough.
65 1f2d1b42 Antony Chazapis
        return unichr(0xffff)
66 1f2d1b42 Antony Chazapis
    s = prefix[:-1]
67 1f2d1b42 Antony Chazapis
    c = ord(prefix[-1])
68 1f2d1b42 Antony Chazapis
    if c >= 0xffff:
69 1f2d1b42 Antony Chazapis
        raise RuntimeError
70 2715ade4 Sofia Papagiannaki
    s += unichr(c + 1)
71 1f2d1b42 Antony Chazapis
    return s
72 1f2d1b42 Antony Chazapis
73 2715ade4 Sofia Papagiannaki
74 1f2d1b42 Antony Chazapis
def strprevling(prefix):
75 60b8a083 Antony Chazapis
    """Return an approximation of the last unicode string
76 1f2d1b42 Antony Chazapis
       less than but not starting with given prefix.
77 1f2d1b42 Antony Chazapis
       strprevling(u'hello') -> u'helln\\xffff'
78 1f2d1b42 Antony Chazapis
    """
79 1f2d1b42 Antony Chazapis
    if not prefix:
80 1f2d1b42 Antony Chazapis
        ## There is no prevling for the null string
81 1f2d1b42 Antony Chazapis
        return prefix
82 1f2d1b42 Antony Chazapis
    s = prefix[:-1]
83 1f2d1b42 Antony Chazapis
    c = ord(prefix[-1])
84 1f2d1b42 Antony Chazapis
    if c > 0:
85 2715ade4 Sofia Papagiannaki
        s += unichr(c - 1) + unichr(0xffff)
86 1f2d1b42 Antony Chazapis
    return s
87 1f2d1b42 Antony Chazapis
88 1f2d1b42 Antony Chazapis
89 1f2d1b42 Antony Chazapis
_propnames = {
90 2715ade4 Sofia Papagiannaki
    'serial': 0,
91 2715ade4 Sofia Papagiannaki
    'node': 1,
92 2715ade4 Sofia Papagiannaki
    'hash': 2,
93 2715ade4 Sofia Papagiannaki
    'size': 3,
94 2715ade4 Sofia Papagiannaki
    'type': 4,
95 2715ade4 Sofia Papagiannaki
    'source': 5,
96 2715ade4 Sofia Papagiannaki
    'mtime': 6,
97 2715ade4 Sofia Papagiannaki
    'muser': 7,
98 2715ade4 Sofia Papagiannaki
    'uuid': 8,
99 2715ade4 Sofia Papagiannaki
    'checksum': 9,
100 2715ade4 Sofia Papagiannaki
    'cluster': 10
101 1f2d1b42 Antony Chazapis
}
102 1f2d1b42 Antony Chazapis
103 1f2d1b42 Antony Chazapis
104 1f2d1b42 Antony Chazapis
class Node(DBWorker):
105 60b8a083 Antony Chazapis
    """Nodes store path organization and have multiple versions.
106 60b8a083 Antony Chazapis
       Versions store object history and have multiple attributes.
107 1f2d1b42 Antony Chazapis
       Attributes store metadata.
108 1f2d1b42 Antony Chazapis
    """
109 2715ade4 Sofia Papagiannaki
110 60b8a083 Antony Chazapis
    # TODO: Provide an interface for included and excluded clusters.
111 2715ade4 Sofia Papagiannaki
112 1f2d1b42 Antony Chazapis
    def __init__(self, **params):
113 62f915a1 Antony Chazapis
        DBWorker.__init__(self, **params)
114 1f2d1b42 Antony Chazapis
        execute = self.execute
115 2715ade4 Sofia Papagiannaki
116 1f2d1b42 Antony Chazapis
        execute(""" pragma foreign_keys = on """)
117 2715ade4 Sofia Papagiannaki
118 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists nodes
119 1f2d1b42 Antony Chazapis
                          ( node       integer primary key,
120 62f915a1 Antony Chazapis
                            parent     integer default 0,
121 44ad5860 Antony Chazapis
                            path       text    not null default '',
122 585b75e7 Sofia Papagiannaki
                            latest_version     integer,
123 1f2d1b42 Antony Chazapis
                            foreign key (parent)
124 1f2d1b42 Antony Chazapis
                            references nodes(node)
125 1f2d1b42 Antony Chazapis
                            on update cascade
126 5e7485da Antony Chazapis
                            on delete cascade ) """)
127 1f2d1b42 Antony Chazapis
        execute(""" create unique index if not exists idx_nodes_path
128 1f2d1b42 Antony Chazapis
                    on nodes(path) """)
129 585b75e7 Sofia Papagiannaki
        execute(""" create index if not exists idx_nodes_parent
130 585b75e7 Sofia Papagiannaki
                    on nodes(parent) """)
131 2715ade4 Sofia Papagiannaki
132 5e7485da Antony Chazapis
        execute(""" create table if not exists policy
133 5e7485da Antony Chazapis
                          ( node   integer,
134 5e7485da Antony Chazapis
                            key    text,
135 5e7485da Antony Chazapis
                            value  text,
136 5e7485da Antony Chazapis
                            primary key (node, key)
137 5e7485da Antony Chazapis
                            foreign key (node)
138 5e7485da Antony Chazapis
                            references nodes(node)
139 5e7485da Antony Chazapis
                            on update cascade
140 5e7485da Antony Chazapis
                            on delete cascade ) """)
141 2715ade4 Sofia Papagiannaki
142 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists statistics
143 62f915a1 Antony Chazapis
                          ( node       integer,
144 1f2d1b42 Antony Chazapis
                            population integer not null default 0,
145 1f2d1b42 Antony Chazapis
                            size       integer not null default 0,
146 1f2d1b42 Antony Chazapis
                            mtime      integer,
147 1f2d1b42 Antony Chazapis
                            cluster    integer not null default 0,
148 1f2d1b42 Antony Chazapis
                            primary key (node, cluster)
149 1f2d1b42 Antony Chazapis
                            foreign key (node)
150 1f2d1b42 Antony Chazapis
                            references nodes(node)
151 1f2d1b42 Antony Chazapis
                            on update cascade
152 5e7485da Antony Chazapis
                            on delete cascade ) """)
153 2715ade4 Sofia Papagiannaki
154 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists versions
155 1f2d1b42 Antony Chazapis
                          ( serial     integer primary key,
156 62f915a1 Antony Chazapis
                            node       integer,
157 1c2fc0ff Antony Chazapis
                            hash       text,
158 1f2d1b42 Antony Chazapis
                            size       integer not null default 0,
159 66ce2ca5 Antony Chazapis
                            type       text    not null default '',
160 1f2d1b42 Antony Chazapis
                            source     integer,
161 1f2d1b42 Antony Chazapis
                            mtime      integer,
162 c1cdc455 Antony Chazapis
                            muser      text    not null default '',
163 25ae8b75 Antony Chazapis
                            uuid       text    not null default '',
164 33b4e4a6 Antony Chazapis
                            checksum   text    not null default '',
165 1f2d1b42 Antony Chazapis
                            cluster    integer not null default 0,
166 1f2d1b42 Antony Chazapis
                            foreign key (node)
167 1f2d1b42 Antony Chazapis
                            references nodes(node)
168 1f2d1b42 Antony Chazapis
                            on update cascade
169 1f2d1b42 Antony Chazapis
                            on delete cascade ) """)
170 9e35988c Antony Chazapis
        execute(""" create index if not exists idx_versions_node_mtime
171 9e35988c Antony Chazapis
                    on versions(node, mtime) """)
172 25ae8b75 Antony Chazapis
        execute(""" create index if not exists idx_versions_node_uuid
173 25ae8b75 Antony Chazapis
                    on versions(uuid) """)
174 2715ade4 Sofia Papagiannaki
175 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists attributes
176 1f2d1b42 Antony Chazapis
                          ( serial integer,
177 4819d34f Antony Chazapis
                            domain text,
178 1f2d1b42 Antony Chazapis
                            key    text,
179 1f2d1b42 Antony Chazapis
                            value  text,
180 4819d34f Antony Chazapis
                            primary key (serial, domain, key)
181 1f2d1b42 Antony Chazapis
                            foreign key (serial)
182 1f2d1b42 Antony Chazapis
                            references versions(serial)
183 1f2d1b42 Antony Chazapis
                            on update cascade
184 1f2d1b42 Antony Chazapis
                            on delete cascade ) """)
185 5576e6dd Sofia Papagiannaki
        execute(""" create index if not exists idx_attributes_domain
186 5576e6dd Sofia Papagiannaki
                    on attributes(domain) """)
187 2715ade4 Sofia Papagiannaki
188 cf53943b Sofia Papagiannaki
        wrapper = self.wrapper
189 cf53943b Sofia Papagiannaki
        wrapper.execute()
190 cf53943b Sofia Papagiannaki
        try:
191 cf53943b Sofia Papagiannaki
            q = "insert or ignore into nodes(node, parent) values (?, ?)"
192 cf53943b Sofia Papagiannaki
            execute(q, (ROOTNODE, ROOTNODE))
193 cf53943b Sofia Papagiannaki
        finally:
194 cf53943b Sofia Papagiannaki
            wrapper.commit()
195 2715ade4 Sofia Papagiannaki
196 44ad5860 Antony Chazapis
    def node_create(self, parent, path):
197 44ad5860 Antony Chazapis
        """Create a new node from the given properties.
198 44ad5860 Antony Chazapis
           Return the node identifier of the new node.
199 44ad5860 Antony Chazapis
        """
200 2715ade4 Sofia Papagiannaki
201 44ad5860 Antony Chazapis
        q = ("insert into nodes (parent, path) "
202 44ad5860 Antony Chazapis
             "values (?, ?)")
203 44ad5860 Antony Chazapis
        props = (parent, path)
204 44ad5860 Antony Chazapis
        return self.execute(q, props).lastrowid
205 2715ade4 Sofia Papagiannaki
206 985b9b09 Sofia Papagiannaki
    def node_lookup(self, path, **kwargs):
207 44ad5860 Antony Chazapis
        """Lookup the current node of the given path.
208 44ad5860 Antony Chazapis
           Return None if the path is not found.
209 985b9b09 Sofia Papagiannaki

210 985b9b09 Sofia Papagiannaki
           kwargs is not used: it is passed for conformance
211 44ad5860 Antony Chazapis
        """
212 2715ade4 Sofia Papagiannaki
213 c1cdc455 Antony Chazapis
        q = "select node from nodes where path = ?"
214 44ad5860 Antony Chazapis
        self.execute(q, (path,))
215 44ad5860 Antony Chazapis
        r = self.fetchone()
216 44ad5860 Antony Chazapis
        if r is not None:
217 44ad5860 Antony Chazapis
            return r[0]
218 44ad5860 Antony Chazapis
        return None
219 2715ade4 Sofia Papagiannaki
220 4d15c94e Sofia Papagiannaki
    def node_lookup_bulk(self, paths):
221 2715ade4 Sofia Papagiannaki
        """Lookup the current nodes for the given paths.
222 4d15c94e Sofia Papagiannaki
           Return () if the path is not found.
223 4d15c94e Sofia Papagiannaki
        """
224 2715ade4 Sofia Papagiannaki
225 4d15c94e Sofia Papagiannaki
        placeholders = ','.join('?' for path in paths)
226 cf4a7a7b Sofia Papagiannaki
        q = "select node from nodes where path in (%s)" % placeholders
227 4d15c94e Sofia Papagiannaki
        self.execute(q, paths)
228 cf4a7a7b Sofia Papagiannaki
        r = self.fetchall()
229 cf4a7a7b Sofia Papagiannaki
        if r is not None:
230 2715ade4 Sofia Papagiannaki
            return [row[0] for row in r]
231 cf4a7a7b Sofia Papagiannaki
        return None
232 2715ade4 Sofia Papagiannaki
233 62f915a1 Antony Chazapis
    def node_get_properties(self, node):
234 62f915a1 Antony Chazapis
        """Return the node's (parent, path).
235 62f915a1 Antony Chazapis
           Return None if the node is not found.
236 62f915a1 Antony Chazapis
        """
237 2715ade4 Sofia Papagiannaki
238 62f915a1 Antony Chazapis
        q = "select parent, path from nodes where node = ?"
239 c1cdc455 Antony Chazapis
        self.execute(q, (node,))
240 62f915a1 Antony Chazapis
        return self.fetchone()
241 2715ade4 Sofia Papagiannaki
242 676edf89 Antony Chazapis
    def node_get_versions(self, node, keys=(), propnames=_propnames):
243 676edf89 Antony Chazapis
        """Return the properties of all versions at node.
244 676edf89 Antony Chazapis
           If keys is empty, return all properties in the order
245 33b4e4a6 Antony Chazapis
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
246 676edf89 Antony Chazapis
        """
247 2715ade4 Sofia Papagiannaki
248 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
249 676edf89 Antony Chazapis
             "from versions "
250 676edf89 Antony Chazapis
             "where node = ?")
251 60b8a083 Antony Chazapis
        self.execute(q, (node,))
252 676edf89 Antony Chazapis
        r = self.fetchall()
253 676edf89 Antony Chazapis
        if r is None:
254 676edf89 Antony Chazapis
            return r
255 2715ade4 Sofia Papagiannaki
256 676edf89 Antony Chazapis
        if not keys:
257 676edf89 Antony Chazapis
            return r
258 676edf89 Antony Chazapis
        return [[p[propnames[k]] for k in keys if k in propnames] for p in r]
259 2715ade4 Sofia Papagiannaki
260 c915d3bf Antony Chazapis
    def node_count_children(self, node):
261 44ad5860 Antony Chazapis
        """Return node's child count."""
262 2715ade4 Sofia Papagiannaki
263 c915d3bf Antony Chazapis
        q = "select count(node) from nodes where parent = ? and node != 0"
264 44ad5860 Antony Chazapis
        self.execute(q, (node,))
265 62f915a1 Antony Chazapis
        r = self.fetchone()
266 44ad5860 Antony Chazapis
        if r is None:
267 44ad5860 Antony Chazapis
            return 0
268 62f915a1 Antony Chazapis
        return r[0]
269 2715ade4 Sofia Papagiannaki
270 c915d3bf Antony Chazapis
    def node_purge_children(self, parent, before=inf, cluster=0):
271 c915d3bf Antony Chazapis
        """Delete all versions with the specified
272 c915d3bf Antony Chazapis
           parent and cluster, and return
273 0a92ff85 Sofia Papagiannaki
           the hashes, the size and the serials of versions deleted.
274 c915d3bf Antony Chazapis
           Clears out nodes with no remaining versions.
275 c915d3bf Antony Chazapis
        """
276 2715ade4 Sofia Papagiannaki
277 c915d3bf Antony Chazapis
        execute = self.execute
278 c915d3bf Antony Chazapis
        q = ("select count(serial), sum(size) from versions "
279 c915d3bf Antony Chazapis
             "where node in (select node "
280 2715ade4 Sofia Papagiannaki
             "from nodes "
281 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
282 c915d3bf Antony Chazapis
             "and cluster = ? "
283 c915d3bf Antony Chazapis
             "and mtime <= ?")
284 c915d3bf Antony Chazapis
        args = (parent, cluster, before)
285 c915d3bf Antony Chazapis
        execute(q, args)
286 c915d3bf Antony Chazapis
        nr, size = self.fetchone()
287 c915d3bf Antony Chazapis
        if not nr:
288 442bc80c Sofia Papagiannaki
            return (), 0, ()
289 60b8a083 Antony Chazapis
        mtime = time()
290 60b8a083 Antony Chazapis
        self.statistics_update(parent, -nr, -size, mtime, cluster)
291 60b8a083 Antony Chazapis
        self.statistics_update_ancestors(parent, -nr, -size, mtime, cluster)
292 2715ade4 Sofia Papagiannaki
293 5576e6dd Sofia Papagiannaki
        q = ("select hash, serial from versions "
294 c915d3bf Antony Chazapis
             "where node in (select node "
295 2715ade4 Sofia Papagiannaki
             "from nodes "
296 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
297 c915d3bf Antony Chazapis
             "and cluster = ? "
298 c915d3bf Antony Chazapis
             "and mtime <= ?")
299 c915d3bf Antony Chazapis
        execute(q, args)
300 388ea25f Sofia Papagiannaki
        hashes = []
301 388ea25f Sofia Papagiannaki
        serials = []
302 388ea25f Sofia Papagiannaki
        for r in self.fetchall():
303 388ea25f Sofia Papagiannaki
            hashes += [r[0]]
304 388ea25f Sofia Papagiannaki
            serials += [r[1]]
305 388ea25f Sofia Papagiannaki
        
306 c915d3bf Antony Chazapis
        q = ("delete from versions "
307 c915d3bf Antony Chazapis
             "where node in (select node "
308 2715ade4 Sofia Papagiannaki
             "from nodes "
309 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
310 c915d3bf Antony Chazapis
             "and cluster = ? "
311 c915d3bf Antony Chazapis
             "and mtime <= ?")
312 c915d3bf Antony Chazapis
        execute(q, args)
313 62f915a1 Antony Chazapis
        q = ("delete from nodes "
314 62f915a1 Antony Chazapis
             "where node in (select node from nodes n "
315 2715ade4 Sofia Papagiannaki
             "where (select count(serial) "
316 2715ade4 Sofia Papagiannaki
             "from versions "
317 2715ade4 Sofia Papagiannaki
             "where node = n.node) = 0 "
318 2715ade4 Sofia Papagiannaki
             "and parent = ?)")
319 62f915a1 Antony Chazapis
        execute(q, (parent,))
320 388ea25f Sofia Papagiannaki
        return hashes, size, serials
321 2715ade4 Sofia Papagiannaki
322 c915d3bf Antony Chazapis
    def node_purge(self, node, before=inf, cluster=0):
323 c915d3bf Antony Chazapis
        """Delete all versions with the specified
324 c915d3bf Antony Chazapis
           node and cluster, and return
325 0a92ff85 Sofia Papagiannaki
           the hashes, the size and the serials of versions deleted.
326 c915d3bf Antony Chazapis
           Clears out the node if it has no remaining versions.
327 c915d3bf Antony Chazapis
        """
328 2715ade4 Sofia Papagiannaki
329 c915d3bf Antony Chazapis
        execute = self.execute
330 c915d3bf Antony Chazapis
        q = ("select count(serial), sum(size) from versions "
331 c915d3bf Antony Chazapis
             "where node = ? "
332 c915d3bf Antony Chazapis
             "and cluster = ? "
333 c915d3bf Antony Chazapis
             "and mtime <= ?")
334 c915d3bf Antony Chazapis
        args = (node, cluster, before)
335 c915d3bf Antony Chazapis
        execute(q, args)
336 c915d3bf Antony Chazapis
        nr, size = self.fetchone()
337 c915d3bf Antony Chazapis
        if not nr:
338 442bc80c Sofia Papagiannaki
            return (), 0, ()
339 60b8a083 Antony Chazapis
        mtime = time()
340 60b8a083 Antony Chazapis
        self.statistics_update_ancestors(node, -nr, -size, mtime, cluster)
341 2715ade4 Sofia Papagiannaki
342 5576e6dd Sofia Papagiannaki
        q = ("select hash, serial from versions "
343 c915d3bf Antony Chazapis
             "where node = ? "
344 c915d3bf Antony Chazapis
             "and cluster = ? "
345 c915d3bf Antony Chazapis
             "and mtime <= ?")
346 c915d3bf Antony Chazapis
        execute(q, args)
347 388ea25f Sofia Papagiannaki
        hashes = []
348 388ea25f Sofia Papagiannaki
        serials = []
349 388ea25f Sofia Papagiannaki
        for r in self.fetchall():
350 388ea25f Sofia Papagiannaki
            hashes += [r[0]]
351 388ea25f Sofia Papagiannaki
            serials += [r[1]]
352 5576e6dd Sofia Papagiannaki
353 c915d3bf Antony Chazapis
        q = ("delete from versions "
354 c915d3bf Antony Chazapis
             "where node = ? "
355 c915d3bf Antony Chazapis
             "and cluster = ? "
356 c915d3bf Antony Chazapis
             "and mtime <= ?")
357 c915d3bf Antony Chazapis
        execute(q, args)
358 62f915a1 Antony Chazapis
        q = ("delete from nodes "
359 62f915a1 Antony Chazapis
             "where node in (select node from nodes n "
360 2715ade4 Sofia Papagiannaki
             "where (select count(serial) "
361 2715ade4 Sofia Papagiannaki
             "from versions "
362 2715ade4 Sofia Papagiannaki
             "where node = n.node) = 0 "
363 2715ade4 Sofia Papagiannaki
             "and node = ?)")
364 62f915a1 Antony Chazapis
        execute(q, (node,))
365 388ea25f Sofia Papagiannaki
        return hashes, size, serials
366 2715ade4 Sofia Papagiannaki
367 c915d3bf Antony Chazapis
    def node_remove(self, node):
368 c915d3bf Antony Chazapis
        """Remove the node specified.
369 c915d3bf Antony Chazapis
           Return false if the node has children or is not found.
370 c915d3bf Antony Chazapis
        """
371 2715ade4 Sofia Papagiannaki
372 c1cdc455 Antony Chazapis
        if self.node_count_children(node):
373 c915d3bf Antony Chazapis
            return False
374 2715ade4 Sofia Papagiannaki
375 c915d3bf Antony Chazapis
        mtime = time()
376 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), cluster "
377 c1cdc455 Antony Chazapis
             "from versions "
378 c1cdc455 Antony Chazapis
             "where node = ? "
379 c1cdc455 Antony Chazapis
             "group by cluster")
380 c915d3bf Antony Chazapis
        self.execute(q, (node,))
381 c915d3bf Antony Chazapis
        for population, size, cluster in self.fetchall():
382 2715ade4 Sofia Papagiannaki
            self.statistics_update_ancestors(
383 2715ade4 Sofia Papagiannaki
                node, -population, -size, mtime, cluster)
384 2715ade4 Sofia Papagiannaki
385 c915d3bf Antony Chazapis
        q = "delete from nodes where node = ?"
386 c915d3bf Antony Chazapis
        self.execute(q, (node,))
387 c915d3bf Antony Chazapis
        return True
388 2715ade4 Sofia Papagiannaki
389 d1e7d2b4 Sofia Papagiannaki
    def node_accounts(self, accounts=()):
390 ae6199c5 Sofia Papagiannaki
        q = ("select path, node from nodes where node != 0 and parent = 0 ")
391 d1e7d2b4 Sofia Papagiannaki
        args = []
392 d1e7d2b4 Sofia Papagiannaki
        if accounts:
393 d1e7d2b4 Sofia Papagiannaki
            placeholders = ','.join('?' for a in accounts)
394 d1e7d2b4 Sofia Papagiannaki
            q += ("and path in (%s)" % placeholders)
395 d1e7d2b4 Sofia Papagiannaki
            args += accounts
396 d1e7d2b4 Sofia Papagiannaki
        return self.execute(q, args).fetchall()
397 d1e7d2b4 Sofia Papagiannaki
398 ae6199c5 Sofia Papagiannaki
    def node_account_quotas(self):
399 ae6199c5 Sofia Papagiannaki
        q = ("select n.path, p.value from nodes n, policy p "
400 ae6199c5 Sofia Papagiannaki
             "where n.node != 0 and n.parent = 0 "
401 ae6199c5 Sofia Papagiannaki
             "and n.node = p.node and p.key = 'quota'"
402 ae6199c5 Sofia Papagiannaki
        )
403 ae6199c5 Sofia Papagiannaki
        return dict(self.execute(q).fetchall())
404 ae6199c5 Sofia Papagiannaki
405 d1e7d2b4 Sofia Papagiannaki
    def node_account_usage(self, account_node, cluster):
406 d1e7d2b4 Sofia Papagiannaki
        select_children = ("select node from nodes where parent = ?")
407 d1e7d2b4 Sofia Papagiannaki
        select_descedents = ("select node from nodes "
408 d1e7d2b4 Sofia Papagiannaki
                             "where parent in (%s) "
409 d1e7d2b4 Sofia Papagiannaki
                             "or node in (%s) ") % ((select_children,)*2)
410 d1e7d2b4 Sofia Papagiannaki
        args = [account_node]*2
411 d1e7d2b4 Sofia Papagiannaki
        q = ("select sum(v.size) from versions v, nodes n "
412 d1e7d2b4 Sofia Papagiannaki
             "where v.node = n.node "
413 d1e7d2b4 Sofia Papagiannaki
             "and n.node in (%s) "
414 d1e7d2b4 Sofia Papagiannaki
             "and v.cluster = ?") % select_descedents
415 d1e7d2b4 Sofia Papagiannaki
        args += [cluster]
416 d1e7d2b4 Sofia Papagiannaki
417 d1e7d2b4 Sofia Papagiannaki
        self.execute(q, args)
418 d1e7d2b4 Sofia Papagiannaki
        return self.fetchone()[0]
419 d1e7d2b4 Sofia Papagiannaki
420 5e7485da Antony Chazapis
    def policy_get(self, node):
421 5e7485da Antony Chazapis
        q = "select key, value from policy where node = ?"
422 5e7485da Antony Chazapis
        self.execute(q, (node,))
423 5e7485da Antony Chazapis
        return dict(self.fetchall())
424 2715ade4 Sofia Papagiannaki
425 5e7485da Antony Chazapis
    def policy_set(self, node, policy):
426 5e7485da Antony Chazapis
        q = "insert or replace into policy (node, key, value) values (?, ?, ?)"
427 5e7485da Antony Chazapis
        self.executemany(q, ((node, k, v) for k, v in policy.iteritems()))
428 2715ade4 Sofia Papagiannaki
429 c1cdc455 Antony Chazapis
    def statistics_get(self, node, cluster=0):
430 c1cdc455 Antony Chazapis
        """Return population, total size and last mtime
431 c1cdc455 Antony Chazapis
           for all versions under node that belong to the cluster.
432 c1cdc455 Antony Chazapis
        """
433 2715ade4 Sofia Papagiannaki
434 62f915a1 Antony Chazapis
        q = ("select population, size, mtime from statistics "
435 c1cdc455 Antony Chazapis
             "where node = ? and cluster = ?")
436 c1cdc455 Antony Chazapis
        self.execute(q, (node, cluster))
437 62f915a1 Antony Chazapis
        return self.fetchone()
438 2715ade4 Sofia Papagiannaki
439 c1cdc455 Antony Chazapis
    def statistics_update(self, node, population, size, mtime, cluster=0):
440 c1cdc455 Antony Chazapis
        """Update the statistics of the given node.
441 c1cdc455 Antony Chazapis
           Statistics keep track the population, total
442 c1cdc455 Antony Chazapis
           size of objects and mtime in the node's namespace.
443 c1cdc455 Antony Chazapis
           May be zero or positive or negative numbers.
444 c1cdc455 Antony Chazapis
        """
445 2715ade4 Sofia Papagiannaki
446 62f915a1 Antony Chazapis
        qs = ("select population, size from statistics "
447 c1cdc455 Antony Chazapis
              "where node = ? and cluster = ?")
448 c1cdc455 Antony Chazapis
        qu = ("insert or replace into statistics (node, population, size, mtime, cluster) "
449 c1cdc455 Antony Chazapis
              "values (?, ?, ?, ?, ?)")
450 c1cdc455 Antony Chazapis
        self.execute(qs, (node, cluster))
451 c1cdc455 Antony Chazapis
        r = self.fetchone()
452 c1cdc455 Antony Chazapis
        if r is None:
453 c1cdc455 Antony Chazapis
            prepopulation, presize = (0, 0)
454 c1cdc455 Antony Chazapis
        else:
455 c1cdc455 Antony Chazapis
            prepopulation, presize = r
456 c1cdc455 Antony Chazapis
        population += prepopulation
457 096a7c3b Sofia Papagiannaki
        population = max(population, 0)
458 c1cdc455 Antony Chazapis
        size += presize
459 c1cdc455 Antony Chazapis
        self.execute(qu, (node, population, size, mtime, cluster))
460 2715ade4 Sofia Papagiannaki
461 c1cdc455 Antony Chazapis
    def statistics_update_ancestors(self, node, population, size, mtime, cluster=0):
462 c1cdc455 Antony Chazapis
        """Update the statistics of the given node's parent.
463 c1cdc455 Antony Chazapis
           Then recursively update all parents up to the root.
464 c1cdc455 Antony Chazapis
           Population is not recursive.
465 c1cdc455 Antony Chazapis
        """
466 2715ade4 Sofia Papagiannaki
467 c1cdc455 Antony Chazapis
        while True:
468 c1cdc455 Antony Chazapis
            if node == 0:
469 c1cdc455 Antony Chazapis
                break
470 62f915a1 Antony Chazapis
            props = self.node_get_properties(node)
471 62f915a1 Antony Chazapis
            if props is None:
472 c1cdc455 Antony Chazapis
                break
473 62f915a1 Antony Chazapis
            parent, path = props
474 62f915a1 Antony Chazapis
            self.statistics_update(parent, population, size, mtime, cluster)
475 62f915a1 Antony Chazapis
            node = parent
476 2715ade4 Sofia Papagiannaki
            population = 0  # Population isn't recursive
477 2715ade4 Sofia Papagiannaki
478 62f915a1 Antony Chazapis
    def statistics_latest(self, node, before=inf, except_cluster=0):
479 62f915a1 Antony Chazapis
        """Return population, total size and last mtime
480 62f915a1 Antony Chazapis
           for all latest versions under node that
481 62f915a1 Antony Chazapis
           do not belong to the cluster.
482 62f915a1 Antony Chazapis
        """
483 2715ade4 Sofia Papagiannaki
484 676edf89 Antony Chazapis
        execute = self.execute
485 676edf89 Antony Chazapis
        fetchone = self.fetchone
486 2715ade4 Sofia Papagiannaki
487 62f915a1 Antony Chazapis
        # The node.
488 62f915a1 Antony Chazapis
        props = self.node_get_properties(node)
489 62f915a1 Antony Chazapis
        if props is None:
490 62f915a1 Antony Chazapis
            return None
491 62f915a1 Antony Chazapis
        parent, path = props
492 2715ade4 Sofia Papagiannaki
493 62f915a1 Antony Chazapis
        # The latest version.
494 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
495 585b75e7 Sofia Papagiannaki
             "from versions v "
496 585b75e7 Sofia Papagiannaki
             "where serial = %s "
497 62f915a1 Antony Chazapis
             "and cluster != ?")
498 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
499 2715ade4 Sofia Papagiannaki
            node=node, before=before)
500 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster])
501 676edf89 Antony Chazapis
        props = fetchone()
502 62f915a1 Antony Chazapis
        if props is None:
503 62f915a1 Antony Chazapis
            return None
504 62f915a1 Antony Chazapis
        mtime = props[MTIME]
505 2715ade4 Sofia Papagiannaki
506 62f915a1 Antony Chazapis
        # First level, just under node (get population).
507 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
508 62f915a1 Antony Chazapis
             "from versions v "
509 585b75e7 Sofia Papagiannaki
             "where serial = %s "
510 62f915a1 Antony Chazapis
             "and cluster != ? "
511 62f915a1 Antony Chazapis
             "and node in (select node "
512 2715ade4 Sofia Papagiannaki
             "from nodes "
513 2715ade4 Sofia Papagiannaki
             "where parent = ?)")
514 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
515 2715ade4 Sofia Papagiannaki
            node=None, before=before)
516 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster, node])
517 62f915a1 Antony Chazapis
        r = fetchone()
518 62f915a1 Antony Chazapis
        if r is None:
519 62f915a1 Antony Chazapis
            return None
520 62f915a1 Antony Chazapis
        count = r[0]
521 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
522 62f915a1 Antony Chazapis
        if count == 0:
523 62f915a1 Antony Chazapis
            return (0, 0, mtime)
524 2715ade4 Sofia Papagiannaki
525 62f915a1 Antony Chazapis
        # All children (get size and mtime).
526 92da0e5a Antony Chazapis
        # This is why the full path is stored.
527 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
528 62f915a1 Antony Chazapis
             "from versions v "
529 585b75e7 Sofia Papagiannaki
             "where serial = %s "
530 62f915a1 Antony Chazapis
             "and cluster != ? "
531 62f915a1 Antony Chazapis
             "and node in (select node "
532 2715ade4 Sofia Papagiannaki
             "from nodes "
533 2715ade4 Sofia Papagiannaki
             "where path like ? escape '\\')")
534 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
535 2715ade4 Sofia Papagiannaki
            node=None, before=before)
536 2715ade4 Sofia Papagiannaki
        execute(
537 2715ade4 Sofia Papagiannaki
            q % subq, args + [except_cluster, self.escape_like(path) + '%'])
538 62f915a1 Antony Chazapis
        r = fetchone()
539 62f915a1 Antony Chazapis
        if r is None:
540 62f915a1 Antony Chazapis
            return None
541 62f915a1 Antony Chazapis
        size = r[1] - props[SIZE]
542 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
543 62f915a1 Antony Chazapis
        return (count, size, mtime)
544 2715ade4 Sofia Papagiannaki
545 585b75e7 Sofia Papagiannaki
    def nodes_set_latest_version(self, node, serial):
546 2715ade4 Sofia Papagiannaki
        q = ("update nodes set latest_version = ? where node = ?")
547 585b75e7 Sofia Papagiannaki
        props = (serial, node)
548 585b75e7 Sofia Papagiannaki
        self.execute(q, props)
549 2715ade4 Sofia Papagiannaki
550 33b4e4a6 Antony Chazapis
    def version_create(self, node, hash, size, type, source, muser, uuid, checksum, cluster=0):
551 44ad5860 Antony Chazapis
        """Create a new version from the given properties.
552 44ad5860 Antony Chazapis
           Return the (serial, mtime) of the new version.
553 44ad5860 Antony Chazapis
        """
554 2715ade4 Sofia Papagiannaki
555 33b4e4a6 Antony Chazapis
        q = ("insert into versions (node, hash, size, type, source, mtime, muser, uuid, checksum, cluster) "
556 33b4e4a6 Antony Chazapis
             "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
557 44ad5860 Antony Chazapis
        mtime = time()
558 2715ade4 Sofia Papagiannaki
        props = (node, hash, size, type, source, mtime, muser,
559 2715ade4 Sofia Papagiannaki
                 uuid, checksum, cluster)
560 44ad5860 Antony Chazapis
        serial = self.execute(q, props).lastrowid
561 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
562 2715ade4 Sofia Papagiannaki
563 585b75e7 Sofia Papagiannaki
        self.nodes_set_latest_version(node, serial)
564 2715ade4 Sofia Papagiannaki
565 44ad5860 Antony Chazapis
        return serial, mtime
566 2715ade4 Sofia Papagiannaki
567 cf4a7a7b Sofia Papagiannaki
    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
568 44ad5860 Antony Chazapis
        """Lookup the current version of the given node.
569 44ad5860 Antony Chazapis
           Return a list with its properties:
570 2715ade4 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime,
571 2715ade4 Sofia Papagiannaki
            muser, uuid, checksum, cluster)
572 44ad5860 Antony Chazapis
           or None if the current version is not found in the given cluster.
573 44ad5860 Antony Chazapis
        """
574 2715ade4 Sofia Papagiannaki
575 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
576 585b75e7 Sofia Papagiannaki
             "from versions v "
577 585b75e7 Sofia Papagiannaki
             "where serial = %s "
578 44ad5860 Antony Chazapis
             "and cluster = ?")
579 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
580 2715ade4 Sofia Papagiannaki
            node=node, before=before)
581 cf4a7a7b Sofia Papagiannaki
        if not all_props:
582 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq)
583 cf4a7a7b Sofia Papagiannaki
        else:
584 585b75e7 Sofia Papagiannaki
            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster", subq)
585 2715ade4 Sofia Papagiannaki
586 585b75e7 Sofia Papagiannaki
        self.execute(q, args + [cluster])
587 44ad5860 Antony Chazapis
        props = self.fetchone()
588 44ad5860 Antony Chazapis
        if props is not None:
589 44ad5860 Antony Chazapis
            return props
590 44ad5860 Antony Chazapis
        return None
591 4d15c94e Sofia Papagiannaki
592 cf4a7a7b Sofia Papagiannaki
    def version_lookup_bulk(self, nodes, before=inf, cluster=0, all_props=True):
593 4d15c94e Sofia Papagiannaki
        """Lookup the current versions of the given nodes.
594 4d15c94e Sofia Papagiannaki
           Return a list with their properties:
595 4d15c94e Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
596 4d15c94e Sofia Papagiannaki
        """
597 2715ade4 Sofia Papagiannaki
598 585b75e7 Sofia Papagiannaki
        if not nodes:
599 2715ade4 Sofia Papagiannaki
            return ()
600 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
601 4d15c94e Sofia Papagiannaki
             "from versions "
602 585b75e7 Sofia Papagiannaki
             "where serial in %s "
603 9ac201e2 Sofia Papagiannaki
             "and cluster = ? %s")
604 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_versions_subquery(
605 2715ade4 Sofia Papagiannaki
            nodes=nodes, before=before)
606 cf4a7a7b Sofia Papagiannaki
        if not all_props:
607 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq, '')
608 cf4a7a7b Sofia Papagiannaki
        else:
609 2715ade4 Sofia Papagiannaki
            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster", subq, 'order by node')
610 2715ade4 Sofia Papagiannaki
611 585b75e7 Sofia Papagiannaki
        args += [cluster]
612 4d15c94e Sofia Papagiannaki
        self.execute(q, args)
613 4d15c94e Sofia Papagiannaki
        return self.fetchall()
614 2715ade4 Sofia Papagiannaki
615 1f2d1b42 Antony Chazapis
    def version_get_properties(self, serial, keys=(), propnames=_propnames):
616 1f2d1b42 Antony Chazapis
        """Return a sequence of values for the properties of
617 1f2d1b42 Antony Chazapis
           the version specified by serial and the keys, in the order given.
618 1f2d1b42 Antony Chazapis
           If keys is empty, return all properties in the order
619 33b4e4a6 Antony Chazapis
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
620 1f2d1b42 Antony Chazapis
        """
621 2715ade4 Sofia Papagiannaki
622 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
623 62f915a1 Antony Chazapis
             "from versions "
624 1f2d1b42 Antony Chazapis
             "where serial = ?")
625 1f2d1b42 Antony Chazapis
        self.execute(q, (serial,))
626 1f2d1b42 Antony Chazapis
        r = self.fetchone()
627 1f2d1b42 Antony Chazapis
        if r is None:
628 1f2d1b42 Antony Chazapis
            return r
629 2715ade4 Sofia Papagiannaki
630 1f2d1b42 Antony Chazapis
        if not keys:
631 1f2d1b42 Antony Chazapis
            return r
632 1f2d1b42 Antony Chazapis
        return [r[propnames[k]] for k in keys if k in propnames]
633 2715ade4 Sofia Papagiannaki
634 33b4e4a6 Antony Chazapis
    def version_put_property(self, serial, key, value):
635 33b4e4a6 Antony Chazapis
        """Set value for the property of version specified by key."""
636 2715ade4 Sofia Papagiannaki
637 33b4e4a6 Antony Chazapis
        if key not in _propnames:
638 33b4e4a6 Antony Chazapis
            return
639 33b4e4a6 Antony Chazapis
        q = "update versions set %s = ? where serial = ?" % key
640 33b4e4a6 Antony Chazapis
        self.execute(q, (value, serial))
641 2715ade4 Sofia Papagiannaki
642 44ad5860 Antony Chazapis
    def version_recluster(self, serial, cluster):
643 44ad5860 Antony Chazapis
        """Move the version into another cluster."""
644 2715ade4 Sofia Papagiannaki
645 62f915a1 Antony Chazapis
        props = self.version_get_properties(serial)
646 62f915a1 Antony Chazapis
        if not props:
647 62f915a1 Antony Chazapis
            return
648 44ad5860 Antony Chazapis
        node = props[NODE]
649 44ad5860 Antony Chazapis
        size = props[SIZE]
650 44ad5860 Antony Chazapis
        oldcluster = props[CLUSTER]
651 44ad5860 Antony Chazapis
        if cluster == oldcluster:
652 44ad5860 Antony Chazapis
            return
653 2715ade4 Sofia Papagiannaki
654 62f915a1 Antony Chazapis
        mtime = time()
655 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster)
656 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
657 2715ade4 Sofia Papagiannaki
658 62f915a1 Antony Chazapis
        q = "update versions set cluster = ? where serial = ?"
659 c915d3bf Antony Chazapis
        self.execute(q, (cluster, serial))
660 2715ade4 Sofia Papagiannaki
661 62f915a1 Antony Chazapis
    def version_remove(self, serial):
662 62f915a1 Antony Chazapis
        """Remove the serial specified."""
663 2715ade4 Sofia Papagiannaki
664 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
665 62f915a1 Antony Chazapis
        if not props:
666 62f915a1 Antony Chazapis
            return
667 62f915a1 Antony Chazapis
        node = props[NODE]
668 5161c672 Antony Chazapis
        hash = props[HASH]
669 62f915a1 Antony Chazapis
        size = props[SIZE]
670 62f915a1 Antony Chazapis
        cluster = props[CLUSTER]
671 2715ade4 Sofia Papagiannaki
672 62f915a1 Antony Chazapis
        mtime = time()
673 62f915a1 Antony Chazapis
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster)
674 2715ade4 Sofia Papagiannaki
675 62f915a1 Antony Chazapis
        q = "delete from versions where serial = ?"
676 62f915a1 Antony Chazapis
        self.execute(q, (serial,))
677 2715ade4 Sofia Papagiannaki
678 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
679 585b75e7 Sofia Papagiannaki
        if props:
680 2715ade4 Sofia Papagiannaki
            self.nodes_set_latest_version(node, props[0])
681 813e42e5 Antony Chazapis
        return hash, size
682 2715ade4 Sofia Papagiannaki
683 4819d34f Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
684 44ad5860 Antony Chazapis
        """Return a list of (key, value) pairs of the version specified by serial.
685 1f2d1b42 Antony Chazapis
           If keys is empty, return all attributes.
686 1f2d1b42 Antony Chazapis
           Othwerise, return only those specified.
687 1f2d1b42 Antony Chazapis
        """
688 2715ade4 Sofia Papagiannaki
689 1f2d1b42 Antony Chazapis
        execute = self.execute
690 1f2d1b42 Antony Chazapis
        if keys:
691 1f2d1b42 Antony Chazapis
            marks = ','.join('?' for k in keys)
692 1f2d1b42 Antony Chazapis
            q = ("select key, value from attributes "
693 4819d34f Antony Chazapis
                 "where key in (%s) and serial = ? and domain = ?" % (marks,))
694 4819d34f Antony Chazapis
            execute(q, keys + (serial, domain))
695 1f2d1b42 Antony Chazapis
        else:
696 059857e2 Antony Chazapis
            q = "select key, value from attributes where serial = ? and domain = ?"
697 059857e2 Antony Chazapis
            execute(q, (serial, domain))
698 1f2d1b42 Antony Chazapis
        return self.fetchall()
699 2715ade4 Sofia Papagiannaki
700 4819d34f Antony Chazapis
    def attribute_set(self, serial, domain, items):
701 44ad5860 Antony Chazapis
        """Set the attributes of the version specified by serial.
702 1f2d1b42 Antony Chazapis
           Receive attributes as an iterable of (key, value) pairs.
703 1f2d1b42 Antony Chazapis
        """
704 2715ade4 Sofia Papagiannaki
705 4819d34f Antony Chazapis
        q = ("insert or replace into attributes (serial, domain, key, value) "
706 4819d34f Antony Chazapis
             "values (?, ?, ?, ?)")
707 4819d34f Antony Chazapis
        self.executemany(q, ((serial, domain, k, v) for k, v in items))
708 2715ade4 Sofia Papagiannaki
709 4819d34f Antony Chazapis
    def attribute_del(self, serial, domain, keys=()):
710 44ad5860 Antony Chazapis
        """Delete attributes of the version specified by serial.
711 1f2d1b42 Antony Chazapis
           If keys is empty, delete all attributes.
712 1f2d1b42 Antony Chazapis
           Otherwise delete those specified.
713 1f2d1b42 Antony Chazapis
        """
714 2715ade4 Sofia Papagiannaki
715 1f2d1b42 Antony Chazapis
        if keys:
716 4819d34f Antony Chazapis
            q = "delete from attributes where serial = ? and domain = ? and key = ?"
717 4819d34f Antony Chazapis
            self.executemany(q, ((serial, domain, key) for key in keys))
718 1f2d1b42 Antony Chazapis
        else:
719 4819d34f Antony Chazapis
            q = "delete from attributes where serial = ? and domain = ?"
720 4819d34f Antony Chazapis
            self.execute(q, (serial, domain))
721 2715ade4 Sofia Papagiannaki
722 44ad5860 Antony Chazapis
    def attribute_copy(self, source, dest):
723 1f2d1b42 Antony Chazapis
        q = ("insert or replace into attributes "
724 4819d34f Antony Chazapis
             "select ?, domain, key, value from attributes "
725 1f2d1b42 Antony Chazapis
             "where serial = ?")
726 1f2d1b42 Antony Chazapis
        self.execute(q, (dest, source))
727 2715ade4 Sofia Papagiannaki
728 4819d34f Antony Chazapis
    def _construct_filters(self, domain, filterq):
729 4819d34f Antony Chazapis
        if not domain or not filterq:
730 60b8a083 Antony Chazapis
            return None, None
731 2715ade4 Sofia Papagiannaki
732 95d47e1a Antony Chazapis
        subqlist = []
733 95d47e1a Antony Chazapis
        append = subqlist.append
734 4819d34f Antony Chazapis
        included, excluded, opers = parse_filters(filterq)
735 95d47e1a Antony Chazapis
        args = []
736 2715ade4 Sofia Papagiannaki
737 95d47e1a Antony Chazapis
        if included:
738 c7c0790f chazapis
            subq = "exists (select 1 from attributes where serial = v.serial and domain = ? and "
739 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in included)) + ")"
740 c7c0790f chazapis
            subq += ")"
741 c7c0790f chazapis
            args += [domain]
742 95d47e1a Antony Chazapis
            args += included
743 95d47e1a Antony Chazapis
            append(subq)
744 2715ade4 Sofia Papagiannaki
745 95d47e1a Antony Chazapis
        if excluded:
746 c7c0790f chazapis
            subq = "not exists (select 1 from attributes where serial = v.serial and domain = ? and "
747 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in excluded)) + ")"
748 c7c0790f chazapis
            subq += ")"
749 c7c0790f chazapis
            args += [domain]
750 95d47e1a Antony Chazapis
            args += excluded
751 95d47e1a Antony Chazapis
            append(subq)
752 2715ade4 Sofia Papagiannaki
753 95d47e1a Antony Chazapis
        if opers:
754 95d47e1a Antony Chazapis
            for k, o, v in opers:
755 6b2f11b4 Antony Chazapis
                subq = "exists (select 1 from attributes where serial = v.serial and domain = ? and "
756 6b2f11b4 Antony Chazapis
                subq += "key = ? and value %s ?" % (o,)
757 6b2f11b4 Antony Chazapis
                subq += ")"
758 6b2f11b4 Antony Chazapis
                args += [domain, k, v]
759 6b2f11b4 Antony Chazapis
                append(subq)
760 2715ade4 Sofia Papagiannaki
761 95d47e1a Antony Chazapis
        if not subqlist:
762 95d47e1a Antony Chazapis
            return None, None
763 2715ade4 Sofia Papagiannaki
764 c7c0790f chazapis
        subq = ' and ' + ' and '.join(subqlist)
765 2715ade4 Sofia Papagiannaki
766 60b8a083 Antony Chazapis
        return subq, args
767 2715ade4 Sofia Papagiannaki
768 60b8a083 Antony Chazapis
    def _construct_paths(self, pathq):
769 60b8a083 Antony Chazapis
        if not pathq:
770 60b8a083 Antony Chazapis
            return None, None
771 2715ade4 Sofia Papagiannaki
772 d57eaad4 Antony Chazapis
        subqlist = []
773 d57eaad4 Antony Chazapis
        args = []
774 d57eaad4 Antony Chazapis
        for path, match in pathq:
775 d57eaad4 Antony Chazapis
            if match == MATCH_PREFIX:
776 d57eaad4 Antony Chazapis
                subqlist.append("n.path like ? escape '\\'")
777 d57eaad4 Antony Chazapis
                args.append(self.escape_like(path) + '%')
778 d57eaad4 Antony Chazapis
            elif match == MATCH_EXACT:
779 d57eaad4 Antony Chazapis
                subqlist.append("n.path = ?")
780 d57eaad4 Antony Chazapis
                args.append(path)
781 2715ade4 Sofia Papagiannaki
782 d57eaad4 Antony Chazapis
        subq = ' and (' + ' or '.join(subqlist) + ')'
783 d57eaad4 Antony Chazapis
        args = tuple(args)
784 2715ade4 Sofia Papagiannaki
785 60b8a083 Antony Chazapis
        return subq, args
786 2715ade4 Sofia Papagiannaki
787 7ff57991 Antony Chazapis
    def _construct_size(self, sizeq):
788 7ff57991 Antony Chazapis
        if not sizeq or len(sizeq) != 2:
789 7ff57991 Antony Chazapis
            return None, None
790 2715ade4 Sofia Papagiannaki
791 7ff57991 Antony Chazapis
        subq = ''
792 7ff57991 Antony Chazapis
        args = []
793 7ff57991 Antony Chazapis
        if sizeq[0]:
794 7ff57991 Antony Chazapis
            subq += " and v.size >= ?"
795 7ff57991 Antony Chazapis
            args += [sizeq[0]]
796 7ff57991 Antony Chazapis
        if sizeq[1]:
797 7ff57991 Antony Chazapis
            subq += " and v.size < ?"
798 7ff57991 Antony Chazapis
            args += [sizeq[1]]
799 2715ade4 Sofia Papagiannaki
800 7ff57991 Antony Chazapis
        return subq, args
801 2715ade4 Sofia Papagiannaki
802 585b75e7 Sofia Papagiannaki
    def _construct_versions_nodes_latest_version_subquery(self, before=inf):
803 585b75e7 Sofia Papagiannaki
        if before == inf:
804 585b75e7 Sofia Papagiannaki
            q = ("n.latest_version ")
805 585b75e7 Sofia Papagiannaki
            args = []
806 585b75e7 Sofia Papagiannaki
        else:
807 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
808 2715ade4 Sofia Papagiannaki
                 "from versions "
809 2715ade4 Sofia Papagiannaki
                 "where node = v.node and mtime < ?) ")
810 585b75e7 Sofia Papagiannaki
            args = [before]
811 585b75e7 Sofia Papagiannaki
        return q, args
812 2715ade4 Sofia Papagiannaki
813 585b75e7 Sofia Papagiannaki
    def _construct_latest_version_subquery(self, node=None, before=inf):
814 585b75e7 Sofia Papagiannaki
        where_cond = "node = v.node"
815 585b75e7 Sofia Papagiannaki
        args = []
816 585b75e7 Sofia Papagiannaki
        if node:
817 585b75e7 Sofia Papagiannaki
            where_cond = "node = ? "
818 585b75e7 Sofia Papagiannaki
            args = [node]
819 2715ade4 Sofia Papagiannaki
820 585b75e7 Sofia Papagiannaki
        if before == inf:
821 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
822 2715ade4 Sofia Papagiannaki
                 "from nodes "
823 2715ade4 Sofia Papagiannaki
                 "where %s) ")
824 585b75e7 Sofia Papagiannaki
        else:
825 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
826 2715ade4 Sofia Papagiannaki
                 "from versions "
827 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ?) ")
828 585b75e7 Sofia Papagiannaki
            args += [before]
829 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
830 2715ade4 Sofia Papagiannaki
831 585b75e7 Sofia Papagiannaki
    def _construct_latest_versions_subquery(self, nodes=(), before=inf):
832 585b75e7 Sofia Papagiannaki
        where_cond = ""
833 585b75e7 Sofia Papagiannaki
        args = []
834 585b75e7 Sofia Papagiannaki
        if nodes:
835 585b75e7 Sofia Papagiannaki
            where_cond = "node in (%s) " % ','.join('?' for node in nodes)
836 585b75e7 Sofia Papagiannaki
            args = nodes
837 2715ade4 Sofia Papagiannaki
838 585b75e7 Sofia Papagiannaki
        if before == inf:
839 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
840 2715ade4 Sofia Papagiannaki
                 "from nodes "
841 2715ade4 Sofia Papagiannaki
                 "where %s ) ")
842 585b75e7 Sofia Papagiannaki
        else:
843 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
844 2715ade4 Sofia Papagiannaki
                 "from versions "
845 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ? group by node) ")
846 585b75e7 Sofia Papagiannaki
            args += [before]
847 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
848 2715ade4 Sofia Papagiannaki
849 78348987 Sofia Papagiannaki
    def latest_attribute_keys(self, parent, domain, before=inf, except_cluster=0, pathq=None):
850 676edf89 Antony Chazapis
        """Return a list with all keys pairs defined
851 676edf89 Antony Chazapis
           for all latest versions under parent that
852 676edf89 Antony Chazapis
           do not belong to the cluster.
853 676edf89 Antony Chazapis
        """
854 2715ade4 Sofia Papagiannaki
855 78348987 Sofia Papagiannaki
        pathq = pathq or []
856 78348987 Sofia Papagiannaki
857 676edf89 Antony Chazapis
        # TODO: Use another table to store before=inf results.
858 60b8a083 Antony Chazapis
        q = ("select distinct a.key "
859 676edf89 Antony Chazapis
             "from attributes a, versions v, nodes n "
860 585b75e7 Sofia Papagiannaki
             "where v.serial = %s "
861 676edf89 Antony Chazapis
             "and v.cluster != ? "
862 676edf89 Antony Chazapis
             "and v.node in (select node "
863 2715ade4 Sofia Papagiannaki
             "from nodes "
864 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
865 676edf89 Antony Chazapis
             "and a.serial = v.serial "
866 4819d34f Antony Chazapis
             "and a.domain = ? "
867 676edf89 Antony Chazapis
             "and n.node = v.node")
868 2715ade4 Sofia Papagiannaki
        subq, subargs = self._construct_latest_version_subquery(
869 2715ade4 Sofia Papagiannaki
            node=None, before=before)
870 585b75e7 Sofia Papagiannaki
        args = subargs + [except_cluster, parent, domain]
871 585b75e7 Sofia Papagiannaki
        q = q % subq
872 60b8a083 Antony Chazapis
        subq, subargs = self._construct_paths(pathq)
873 60b8a083 Antony Chazapis
        if subq is not None:
874 60b8a083 Antony Chazapis
            q += subq
875 60b8a083 Antony Chazapis
            args += subargs
876 676edf89 Antony Chazapis
        self.execute(q, args)
877 676edf89 Antony Chazapis
        return [r[0] for r in self.fetchall()]
878 2715ade4 Sofia Papagiannaki
879 60b8a083 Antony Chazapis
    def latest_version_list(self, parent, prefix='', delimiter=None,
880 60b8a083 Antony Chazapis
                            start='', limit=10000, before=inf,
881 371d907a Antony Chazapis
                            except_cluster=0, pathq=[], domain=None,
882 371d907a Antony Chazapis
                            filterq=[], sizeq=None, all_props=False):
883 60b8a083 Antony Chazapis
        """Return a (list of (path, serial) tuples, list of common prefixes)
884 60b8a083 Antony Chazapis
           for the current versions of the paths with the given parent,
885 60b8a083 Antony Chazapis
           matching the following criteria.
886 2715ade4 Sofia Papagiannaki

887 60b8a083 Antony Chazapis
           The property tuple for a version is returned if all
888 60b8a083 Antony Chazapis
           of these conditions are true:
889 2715ade4 Sofia Papagiannaki

890 60b8a083 Antony Chazapis
                a. parent matches
891 2715ade4 Sofia Papagiannaki

892 60b8a083 Antony Chazapis
                b. path > start
893 2715ade4 Sofia Papagiannaki

894 60b8a083 Antony Chazapis
                c. path starts with prefix (and paths in pathq)
895 2715ade4 Sofia Papagiannaki

896 60b8a083 Antony Chazapis
                d. version is the max up to before
897 2715ade4 Sofia Papagiannaki

898 60b8a083 Antony Chazapis
                e. version is not in cluster
899 2715ade4 Sofia Papagiannaki

900 60b8a083 Antony Chazapis
                f. the path does not have the delimiter occuring
901 60b8a083 Antony Chazapis
                   after the prefix, or ends with the delimiter
902 2715ade4 Sofia Papagiannaki

903 60b8a083 Antony Chazapis
                g. serial matches the attribute filter query.
904 2715ade4 Sofia Papagiannaki

905 60b8a083 Antony Chazapis
                   A filter query is a comma-separated list of
906 60b8a083 Antony Chazapis
                   terms in one of these three forms:
907 2715ade4 Sofia Papagiannaki

908 60b8a083 Antony Chazapis
                   key
909 60b8a083 Antony Chazapis
                       an attribute with this key must exist
910 2715ade4 Sofia Papagiannaki

911 60b8a083 Antony Chazapis
                   !key
912 60b8a083 Antony Chazapis
                       an attribute with this key must not exist
913 2715ade4 Sofia Papagiannaki

914 60b8a083 Antony Chazapis
                   key ?op value
915 60b8a083 Antony Chazapis
                       the attribute with this key satisfies the value
916 1713c946 Antony Chazapis
                       where ?op is one of =, != <=, >=, <, >.
917 2715ade4 Sofia Papagiannaki

918 7ff57991 Antony Chazapis
                h. the size is in the range set by sizeq
919 2715ade4 Sofia Papagiannaki

920 60b8a083 Antony Chazapis
           The list of common prefixes includes the prefixes
921 60b8a083 Antony Chazapis
           matching up to the first delimiter after prefix,
922 60b8a083 Antony Chazapis
           and are reported only once, as "virtual directories".
923 60b8a083 Antony Chazapis
           The delimiter is included in the prefixes.
924 2715ade4 Sofia Papagiannaki

925 60b8a083 Antony Chazapis
           If arguments are None, then the corresponding matching rule
926 60b8a083 Antony Chazapis
           will always match.
927 2715ade4 Sofia Papagiannaki

928 60b8a083 Antony Chazapis
           Limit applies to the first list of tuples returned.
929 2715ade4 Sofia Papagiannaki

930 371d907a Antony Chazapis
           If all_props is True, return all properties after path, not just serial.
931 60b8a083 Antony Chazapis
        """
932 2715ade4 Sofia Papagiannaki
933 60b8a083 Antony Chazapis
        execute = self.execute
934 2715ade4 Sofia Papagiannaki
935 60b8a083 Antony Chazapis
        if not start or start < prefix:
936 60b8a083 Antony Chazapis
            start = strprevling(prefix)
937 60b8a083 Antony Chazapis
        nextling = strnextling(prefix)
938 2715ade4 Sofia Papagiannaki
939 371d907a Antony Chazapis
        q = ("select distinct n.path, %s "
940 c7c0790f chazapis
             "from versions v, nodes n "
941 585b75e7 Sofia Papagiannaki
             "where v.serial = %s "
942 60b8a083 Antony Chazapis
             "and v.cluster != ? "
943 60b8a083 Antony Chazapis
             "and v.node in (select node "
944 2715ade4 Sofia Papagiannaki
             "from nodes "
945 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
946 60b8a083 Antony Chazapis
             "and n.node = v.node "
947 60b8a083 Antony Chazapis
             "and n.path > ? and n.path < ?")
948 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_versions_nodes_latest_version_subquery(
949 2715ade4 Sofia Papagiannaki
            before)
950 371d907a Antony Chazapis
        if not all_props:
951 585b75e7 Sofia Papagiannaki
            q = q % ("v.serial", subq)
952 371d907a Antony Chazapis
        else:
953 585b75e7 Sofia Papagiannaki
            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)
954 585b75e7 Sofia Papagiannaki
        args += [except_cluster, parent, start, nextling]
955 585b75e7 Sofia Papagiannaki
        start_index = len(args) - 2
956 2715ade4 Sofia Papagiannaki
957 60b8a083 Antony Chazapis
        subq, subargs = self._construct_paths(pathq)
958 60b8a083 Antony Chazapis
        if subq is not None:
959 60b8a083 Antony Chazapis
            q += subq
960 60b8a083 Antony Chazapis
            args += subargs
961 7ff57991 Antony Chazapis
        subq, subargs = self._construct_size(sizeq)
962 7ff57991 Antony Chazapis
        if subq is not None:
963 7ff57991 Antony Chazapis
            q += subq
964 7ff57991 Antony Chazapis
            args += subargs
965 4819d34f Antony Chazapis
        subq, subargs = self._construct_filters(domain, filterq)
966 60b8a083 Antony Chazapis
        if subq is not None:
967 60b8a083 Antony Chazapis
            q += subq
968 60b8a083 Antony Chazapis
            args += subargs
969 60b8a083 Antony Chazapis
        else:
970 60b8a083 Antony Chazapis
            q = q.replace("attributes a, ", "")
971 60b8a083 Antony Chazapis
            q = q.replace("and a.serial = v.serial ", "")
972 60b8a083 Antony Chazapis
        q += " order by n.path"
973 2715ade4 Sofia Papagiannaki
974 60b8a083 Antony Chazapis
        if not delimiter:
975 60b8a083 Antony Chazapis
            q += " limit ?"
976 60b8a083 Antony Chazapis
            args.append(limit)
977 60b8a083 Antony Chazapis
            execute(q, args)
978 60b8a083 Antony Chazapis
            return self.fetchall(), ()
979 2715ade4 Sofia Papagiannaki
980 60b8a083 Antony Chazapis
        pfz = len(prefix)
981 60b8a083 Antony Chazapis
        dz = len(delimiter)
982 60b8a083 Antony Chazapis
        count = 0
983 60b8a083 Antony Chazapis
        fetchone = self.fetchone
984 60b8a083 Antony Chazapis
        prefixes = []
985 60b8a083 Antony Chazapis
        pappend = prefixes.append
986 60b8a083 Antony Chazapis
        matches = []
987 60b8a083 Antony Chazapis
        mappend = matches.append
988 2715ade4 Sofia Papagiannaki
989 60b8a083 Antony Chazapis
        execute(q, args)
990 60b8a083 Antony Chazapis
        while True:
991 60b8a083 Antony Chazapis
            props = fetchone()
992 60b8a083 Antony Chazapis
            if props is None:
993 60b8a083 Antony Chazapis
                break
994 371d907a Antony Chazapis
            path = props[0]
995 371d907a Antony Chazapis
            serial = props[1]
996 60b8a083 Antony Chazapis
            idx = path.find(delimiter, pfz)
997 2715ade4 Sofia Papagiannaki
998 60b8a083 Antony Chazapis
            if idx < 0:
999 60b8a083 Antony Chazapis
                mappend(props)
1000 60b8a083 Antony Chazapis
                count += 1
1001 60b8a083 Antony Chazapis
                if count >= limit:
1002 60b8a083 Antony Chazapis
                    break
1003 60b8a083 Antony Chazapis
                continue
1004 2715ade4 Sofia Papagiannaki
1005 60b8a083 Antony Chazapis
            if idx + dz == len(path):
1006 60b8a083 Antony Chazapis
                mappend(props)
1007 60b8a083 Antony Chazapis
                count += 1
1008 2715ade4 Sofia Papagiannaki
                continue  # Get one more, in case there is a path.
1009 4cc00e08 Antony Chazapis
            pf = path[:idx + dz]
1010 4cc00e08 Antony Chazapis
            pappend(pf)
1011 2715ade4 Sofia Papagiannaki
            if count >= limit:
1012 60b8a083 Antony Chazapis
                break
1013 2715ade4 Sofia Papagiannaki
1014 2715ade4 Sofia Papagiannaki
            args[start_index] = strnextling(pf)  # New start.
1015 60b8a083 Antony Chazapis
            execute(q, args)
1016 2715ade4 Sofia Papagiannaki
1017 60b8a083 Antony Chazapis
        return matches, prefixes
1018 2715ade4 Sofia Papagiannaki
1019 2bbf1544 Georgios D. Tsoukalas
    def latest_uuid(self, uuid, cluster):
1020 2bbf1544 Georgios D. Tsoukalas
        """Return the latest version of the given uuid and cluster.
1021 2bbf1544 Georgios D. Tsoukalas

1022 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1023 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1024 2bbf1544 Georgios D. Tsoukalas

1025 2bbf1544 Georgios D. Tsoukalas
        """
1026 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
1027 2bbf1544 Georgios D. Tsoukalas
            cluster_where = "and cluster = ?"
1028 2bbf1544 Georgios D. Tsoukalas
            args = (uuid, int(cluster))
1029 2bbf1544 Georgios D. Tsoukalas
        else:
1030 2bbf1544 Georgios D. Tsoukalas
            cluster_where = ""
1031 2bbf1544 Georgios D. Tsoukalas
            args = (uuid,)
1032 2715ade4 Sofia Papagiannaki
1033 37bee317 Antony Chazapis
        q = ("select n.path, v.serial "
1034 37bee317 Antony Chazapis
             "from versions v, nodes n "
1035 37bee317 Antony Chazapis
             "where v.serial = (select max(serial) "
1036 2715ade4 Sofia Papagiannaki
             "from versions "
1037 2bbf1544 Georgios D. Tsoukalas
             "where uuid = ? %s) "
1038 2bbf1544 Georgios D. Tsoukalas
             "and n.node = v.node") % cluster_where
1039 2bbf1544 Georgios D. Tsoukalas
        self.execute(q, args)
1040 37bee317 Antony Chazapis
        return self.fetchone()
1041 5576e6dd Sofia Papagiannaki
1042 7736e11a Sofia Papagiannaki
    def domain_object_list(self, domain, paths, cluster=None):
1043 5576e6dd Sofia Papagiannaki
        """Return a list of (path, property list, attribute dictionary)
1044 5576e6dd Sofia Papagiannaki
           for the objects in the specific domain and cluster.
1045 5576e6dd Sofia Papagiannaki
        """
1046 5576e6dd Sofia Papagiannaki
1047 5576e6dd Sofia Papagiannaki
        q = ("select n.path, v.serial, v.node, v.hash, "
1048 5576e6dd Sofia Papagiannaki
             "v.size, v.type, v.source, v.mtime, v.muser, "
1049 5576e6dd Sofia Papagiannaki
             "v.uuid, v.checksum, v.cluster, a.key, a.value "
1050 5576e6dd Sofia Papagiannaki
             "from nodes n, versions v, attributes a "
1051 5576e6dd Sofia Papagiannaki
             "where n.node = v.node and "
1052 5576e6dd Sofia Papagiannaki
             "n.latest_version = v.serial and "
1053 5576e6dd Sofia Papagiannaki
             "v.serial = a.serial and "
1054 7736e11a Sofia Papagiannaki
             "a.domain = ? and "
1055 a5b0519c Georgios D. Tsoukalas
             "n.path in (%s)") % ','.join('?' for _ in paths)
1056 5576e6dd Sofia Papagiannaki
        args = [domain]
1057 7736e11a Sofia Papagiannaki
        map(args.append, paths)
1058 5576e6dd Sofia Papagiannaki
        if cluster != None:
1059 5576e6dd Sofia Papagiannaki
            q += "and v.cluster = ?"
1060 5576e6dd Sofia Papagiannaki
            args += [cluster]
1061 5576e6dd Sofia Papagiannaki
1062 5576e6dd Sofia Papagiannaki
        self.execute(q, args)
1063 5576e6dd Sofia Papagiannaki
        rows = self.fetchall()
1064 5576e6dd Sofia Papagiannaki
1065 5576e6dd Sofia Papagiannaki
        group_by = itemgetter(slice(12))
1066 5576e6dd Sofia Papagiannaki
        rows.sort(key = group_by)
1067 5576e6dd Sofia Papagiannaki
        groups = groupby(rows, group_by)
1068 5576e6dd Sofia Papagiannaki
        return [(k[0], k[1:], dict([i[12:] for i in data])) \
1069 5576e6dd Sofia Papagiannaki
            for (k, data) in groups]