Statistics
| Branch: | Tag: | Revision:

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

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

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

881 60b8a083 Antony Chazapis
                a. parent matches
882 2715ade4 Sofia Papagiannaki

883 60b8a083 Antony Chazapis
                b. path > start
884 2715ade4 Sofia Papagiannaki

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

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

889 60b8a083 Antony Chazapis
                e. version is not in cluster
890 2715ade4 Sofia Papagiannaki

891 60b8a083 Antony Chazapis
                f. the path does not have the delimiter occuring
892 60b8a083 Antony Chazapis
                   after the prefix, or ends with the delimiter
893 2715ade4 Sofia Papagiannaki

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

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

899 60b8a083 Antony Chazapis
                   key
900 60b8a083 Antony Chazapis
                       an attribute with this key must exist
901 2715ade4 Sofia Papagiannaki

902 60b8a083 Antony Chazapis
                   !key
903 60b8a083 Antony Chazapis
                       an attribute with this key must not exist
904 2715ade4 Sofia Papagiannaki

905 60b8a083 Antony Chazapis
                   key ?op value
906 60b8a083 Antony Chazapis
                       the attribute with this key satisfies the value
907 1713c946 Antony Chazapis
                       where ?op is one of =, != <=, >=, <, >.
908 2715ade4 Sofia Papagiannaki

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

911 60b8a083 Antony Chazapis
           The list of common prefixes includes the prefixes
912 60b8a083 Antony Chazapis
           matching up to the first delimiter after prefix,
913 60b8a083 Antony Chazapis
           and are reported only once, as "virtual directories".
914 60b8a083 Antony Chazapis
           The delimiter is included in the prefixes.
915 2715ade4 Sofia Papagiannaki

916 60b8a083 Antony Chazapis
           If arguments are None, then the corresponding matching rule
917 60b8a083 Antony Chazapis
           will always match.
918 2715ade4 Sofia Papagiannaki

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

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

1013 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1014 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1015 2bbf1544 Georgios D. Tsoukalas

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