Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (36.6 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 ae6199c5 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 ae6199c5 Sofia Papagiannaki
    def node_account_quotas(self):
397 ae6199c5 Sofia Papagiannaki
        q = ("select n.path, p.value from nodes n, policy p "
398 ae6199c5 Sofia Papagiannaki
             "where n.node != 0 and n.parent = 0 "
399 ae6199c5 Sofia Papagiannaki
             "and n.node = p.node and p.key = 'quota'"
400 ae6199c5 Sofia Papagiannaki
        )
401 ae6199c5 Sofia Papagiannaki
        return dict(self.execute(q).fetchall())
402 ae6199c5 Sofia Papagiannaki
403 d1e7d2b4 Sofia Papagiannaki
    def node_account_usage(self, account_node, cluster):
404 d1e7d2b4 Sofia Papagiannaki
        select_children = ("select node from nodes where parent = ?")
405 d1e7d2b4 Sofia Papagiannaki
        select_descedents = ("select node from nodes "
406 d1e7d2b4 Sofia Papagiannaki
                             "where parent in (%s) "
407 d1e7d2b4 Sofia Papagiannaki
                             "or node in (%s) ") % ((select_children,)*2)
408 d1e7d2b4 Sofia Papagiannaki
        args = [account_node]*2
409 d1e7d2b4 Sofia Papagiannaki
        q = ("select sum(v.size) from versions v, nodes n "
410 d1e7d2b4 Sofia Papagiannaki
             "where v.node = n.node "
411 d1e7d2b4 Sofia Papagiannaki
             "and n.node in (%s) "
412 d1e7d2b4 Sofia Papagiannaki
             "and v.cluster = ?") % select_descedents
413 d1e7d2b4 Sofia Papagiannaki
        args += [cluster]
414 d1e7d2b4 Sofia Papagiannaki
415 d1e7d2b4 Sofia Papagiannaki
        self.execute(q, args)
416 d1e7d2b4 Sofia Papagiannaki
        return self.fetchone()[0]
417 d1e7d2b4 Sofia Papagiannaki
418 5e7485da Antony Chazapis
    def policy_get(self, node):
419 5e7485da Antony Chazapis
        q = "select key, value from policy where node = ?"
420 5e7485da Antony Chazapis
        self.execute(q, (node,))
421 5e7485da Antony Chazapis
        return dict(self.fetchall())
422 2715ade4 Sofia Papagiannaki
423 5e7485da Antony Chazapis
    def policy_set(self, node, policy):
424 5e7485da Antony Chazapis
        q = "insert or replace into policy (node, key, value) values (?, ?, ?)"
425 5e7485da Antony Chazapis
        self.executemany(q, ((node, k, v) for k, v in policy.iteritems()))
426 2715ade4 Sofia Papagiannaki
427 c1cdc455 Antony Chazapis
    def statistics_get(self, node, cluster=0):
428 c1cdc455 Antony Chazapis
        """Return population, total size and last mtime
429 c1cdc455 Antony Chazapis
           for all versions under node that belong to the cluster.
430 c1cdc455 Antony Chazapis
        """
431 2715ade4 Sofia Papagiannaki
432 62f915a1 Antony Chazapis
        q = ("select population, size, mtime from statistics "
433 c1cdc455 Antony Chazapis
             "where node = ? and cluster = ?")
434 c1cdc455 Antony Chazapis
        self.execute(q, (node, cluster))
435 62f915a1 Antony Chazapis
        return self.fetchone()
436 2715ade4 Sofia Papagiannaki
437 c1cdc455 Antony Chazapis
    def statistics_update(self, node, population, size, mtime, cluster=0):
438 c1cdc455 Antony Chazapis
        """Update the statistics of the given node.
439 c1cdc455 Antony Chazapis
           Statistics keep track the population, total
440 c1cdc455 Antony Chazapis
           size of objects and mtime in the node's namespace.
441 c1cdc455 Antony Chazapis
           May be zero or positive or negative numbers.
442 c1cdc455 Antony Chazapis
        """
443 2715ade4 Sofia Papagiannaki
444 62f915a1 Antony Chazapis
        qs = ("select population, size from statistics "
445 c1cdc455 Antony Chazapis
              "where node = ? and cluster = ?")
446 c1cdc455 Antony Chazapis
        qu = ("insert or replace into statistics (node, population, size, mtime, cluster) "
447 c1cdc455 Antony Chazapis
              "values (?, ?, ?, ?, ?)")
448 c1cdc455 Antony Chazapis
        self.execute(qs, (node, cluster))
449 c1cdc455 Antony Chazapis
        r = self.fetchone()
450 c1cdc455 Antony Chazapis
        if r is None:
451 c1cdc455 Antony Chazapis
            prepopulation, presize = (0, 0)
452 c1cdc455 Antony Chazapis
        else:
453 c1cdc455 Antony Chazapis
            prepopulation, presize = r
454 c1cdc455 Antony Chazapis
        population += prepopulation
455 096a7c3b Sofia Papagiannaki
        population = max(population, 0)
456 c1cdc455 Antony Chazapis
        size += presize
457 c1cdc455 Antony Chazapis
        self.execute(qu, (node, population, size, mtime, cluster))
458 2715ade4 Sofia Papagiannaki
459 c1cdc455 Antony Chazapis
    def statistics_update_ancestors(self, node, population, size, mtime, cluster=0):
460 c1cdc455 Antony Chazapis
        """Update the statistics of the given node's parent.
461 c1cdc455 Antony Chazapis
           Then recursively update all parents up to the root.
462 c1cdc455 Antony Chazapis
           Population is not recursive.
463 c1cdc455 Antony Chazapis
        """
464 2715ade4 Sofia Papagiannaki
465 c1cdc455 Antony Chazapis
        while True:
466 c1cdc455 Antony Chazapis
            if node == 0:
467 c1cdc455 Antony Chazapis
                break
468 62f915a1 Antony Chazapis
            props = self.node_get_properties(node)
469 62f915a1 Antony Chazapis
            if props is None:
470 c1cdc455 Antony Chazapis
                break
471 62f915a1 Antony Chazapis
            parent, path = props
472 62f915a1 Antony Chazapis
            self.statistics_update(parent, population, size, mtime, cluster)
473 62f915a1 Antony Chazapis
            node = parent
474 2715ade4 Sofia Papagiannaki
            population = 0  # Population isn't recursive
475 2715ade4 Sofia Papagiannaki
476 62f915a1 Antony Chazapis
    def statistics_latest(self, node, before=inf, except_cluster=0):
477 62f915a1 Antony Chazapis
        """Return population, total size and last mtime
478 62f915a1 Antony Chazapis
           for all latest versions under node that
479 62f915a1 Antony Chazapis
           do not belong to the cluster.
480 62f915a1 Antony Chazapis
        """
481 2715ade4 Sofia Papagiannaki
482 676edf89 Antony Chazapis
        execute = self.execute
483 676edf89 Antony Chazapis
        fetchone = self.fetchone
484 2715ade4 Sofia Papagiannaki
485 62f915a1 Antony Chazapis
        # The node.
486 62f915a1 Antony Chazapis
        props = self.node_get_properties(node)
487 62f915a1 Antony Chazapis
        if props is None:
488 62f915a1 Antony Chazapis
            return None
489 62f915a1 Antony Chazapis
        parent, path = props
490 2715ade4 Sofia Papagiannaki
491 62f915a1 Antony Chazapis
        # The latest version.
492 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
493 585b75e7 Sofia Papagiannaki
             "from versions v "
494 585b75e7 Sofia Papagiannaki
             "where serial = %s "
495 62f915a1 Antony Chazapis
             "and cluster != ?")
496 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
497 2715ade4 Sofia Papagiannaki
            node=node, before=before)
498 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster])
499 676edf89 Antony Chazapis
        props = fetchone()
500 62f915a1 Antony Chazapis
        if props is None:
501 62f915a1 Antony Chazapis
            return None
502 62f915a1 Antony Chazapis
        mtime = props[MTIME]
503 2715ade4 Sofia Papagiannaki
504 62f915a1 Antony Chazapis
        # First level, just under node (get population).
505 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
506 62f915a1 Antony Chazapis
             "from versions v "
507 585b75e7 Sofia Papagiannaki
             "where serial = %s "
508 62f915a1 Antony Chazapis
             "and cluster != ? "
509 62f915a1 Antony Chazapis
             "and node in (select node "
510 2715ade4 Sofia Papagiannaki
             "from nodes "
511 2715ade4 Sofia Papagiannaki
             "where parent = ?)")
512 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
513 2715ade4 Sofia Papagiannaki
            node=None, before=before)
514 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster, node])
515 62f915a1 Antony Chazapis
        r = fetchone()
516 62f915a1 Antony Chazapis
        if r is None:
517 62f915a1 Antony Chazapis
            return None
518 62f915a1 Antony Chazapis
        count = r[0]
519 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
520 62f915a1 Antony Chazapis
        if count == 0:
521 62f915a1 Antony Chazapis
            return (0, 0, mtime)
522 2715ade4 Sofia Papagiannaki
523 62f915a1 Antony Chazapis
        # All children (get size and mtime).
524 92da0e5a Antony Chazapis
        # This is why the full path is stored.
525 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
526 62f915a1 Antony Chazapis
             "from versions v "
527 585b75e7 Sofia Papagiannaki
             "where serial = %s "
528 62f915a1 Antony Chazapis
             "and cluster != ? "
529 62f915a1 Antony Chazapis
             "and node in (select node "
530 2715ade4 Sofia Papagiannaki
             "from nodes "
531 2715ade4 Sofia Papagiannaki
             "where path like ? escape '\\')")
532 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
533 2715ade4 Sofia Papagiannaki
            node=None, before=before)
534 2715ade4 Sofia Papagiannaki
        execute(
535 2715ade4 Sofia Papagiannaki
            q % subq, args + [except_cluster, self.escape_like(path) + '%'])
536 62f915a1 Antony Chazapis
        r = fetchone()
537 62f915a1 Antony Chazapis
        if r is None:
538 62f915a1 Antony Chazapis
            return None
539 62f915a1 Antony Chazapis
        size = r[1] - props[SIZE]
540 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
541 62f915a1 Antony Chazapis
        return (count, size, mtime)
542 2715ade4 Sofia Papagiannaki
543 585b75e7 Sofia Papagiannaki
    def nodes_set_latest_version(self, node, serial):
544 2715ade4 Sofia Papagiannaki
        q = ("update nodes set latest_version = ? where node = ?")
545 585b75e7 Sofia Papagiannaki
        props = (serial, node)
546 585b75e7 Sofia Papagiannaki
        self.execute(q, props)
547 2715ade4 Sofia Papagiannaki
548 33b4e4a6 Antony Chazapis
    def version_create(self, node, hash, size, type, source, muser, uuid, checksum, cluster=0):
549 44ad5860 Antony Chazapis
        """Create a new version from the given properties.
550 44ad5860 Antony Chazapis
           Return the (serial, mtime) of the new version.
551 44ad5860 Antony Chazapis
        """
552 2715ade4 Sofia Papagiannaki
553 33b4e4a6 Antony Chazapis
        q = ("insert into versions (node, hash, size, type, source, mtime, muser, uuid, checksum, cluster) "
554 33b4e4a6 Antony Chazapis
             "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
555 44ad5860 Antony Chazapis
        mtime = time()
556 2715ade4 Sofia Papagiannaki
        props = (node, hash, size, type, source, mtime, muser,
557 2715ade4 Sofia Papagiannaki
                 uuid, checksum, cluster)
558 44ad5860 Antony Chazapis
        serial = self.execute(q, props).lastrowid
559 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
560 2715ade4 Sofia Papagiannaki
561 585b75e7 Sofia Papagiannaki
        self.nodes_set_latest_version(node, serial)
562 2715ade4 Sofia Papagiannaki
563 44ad5860 Antony Chazapis
        return serial, mtime
564 2715ade4 Sofia Papagiannaki
565 cf4a7a7b Sofia Papagiannaki
    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
566 44ad5860 Antony Chazapis
        """Lookup the current version of the given node.
567 44ad5860 Antony Chazapis
           Return a list with its properties:
568 2715ade4 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime,
569 2715ade4 Sofia Papagiannaki
            muser, uuid, checksum, cluster)
570 44ad5860 Antony Chazapis
           or None if the current version is not found in the given cluster.
571 44ad5860 Antony Chazapis
        """
572 2715ade4 Sofia Papagiannaki
573 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
574 585b75e7 Sofia Papagiannaki
             "from versions v "
575 585b75e7 Sofia Papagiannaki
             "where serial = %s "
576 44ad5860 Antony Chazapis
             "and cluster = ?")
577 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
578 2715ade4 Sofia Papagiannaki
            node=node, before=before)
579 cf4a7a7b Sofia Papagiannaki
        if not all_props:
580 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq)
581 cf4a7a7b Sofia Papagiannaki
        else:
582 585b75e7 Sofia Papagiannaki
            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster", subq)
583 2715ade4 Sofia Papagiannaki
584 585b75e7 Sofia Papagiannaki
        self.execute(q, args + [cluster])
585 44ad5860 Antony Chazapis
        props = self.fetchone()
586 44ad5860 Antony Chazapis
        if props is not None:
587 44ad5860 Antony Chazapis
            return props
588 44ad5860 Antony Chazapis
        return None
589 4d15c94e Sofia Papagiannaki
590 cf4a7a7b Sofia Papagiannaki
    def version_lookup_bulk(self, nodes, before=inf, cluster=0, all_props=True):
591 4d15c94e Sofia Papagiannaki
        """Lookup the current versions of the given nodes.
592 4d15c94e Sofia Papagiannaki
           Return a list with their properties:
593 4d15c94e Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
594 4d15c94e Sofia Papagiannaki
        """
595 2715ade4 Sofia Papagiannaki
596 585b75e7 Sofia Papagiannaki
        if not nodes:
597 2715ade4 Sofia Papagiannaki
            return ()
598 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
599 4d15c94e Sofia Papagiannaki
             "from versions "
600 585b75e7 Sofia Papagiannaki
             "where serial in %s "
601 9ac201e2 Sofia Papagiannaki
             "and cluster = ? %s")
602 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_versions_subquery(
603 2715ade4 Sofia Papagiannaki
            nodes=nodes, before=before)
604 cf4a7a7b Sofia Papagiannaki
        if not all_props:
605 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq, '')
606 cf4a7a7b Sofia Papagiannaki
        else:
607 2715ade4 Sofia Papagiannaki
            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster", subq, 'order by node')
608 2715ade4 Sofia Papagiannaki
609 585b75e7 Sofia Papagiannaki
        args += [cluster]
610 4d15c94e Sofia Papagiannaki
        self.execute(q, args)
611 4d15c94e Sofia Papagiannaki
        return self.fetchall()
612 2715ade4 Sofia Papagiannaki
613 1f2d1b42 Antony Chazapis
    def version_get_properties(self, serial, keys=(), propnames=_propnames):
614 1f2d1b42 Antony Chazapis
        """Return a sequence of values for the properties of
615 1f2d1b42 Antony Chazapis
           the version specified by serial and the keys, in the order given.
616 1f2d1b42 Antony Chazapis
           If keys is empty, return all properties in the order
617 33b4e4a6 Antony Chazapis
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
618 1f2d1b42 Antony Chazapis
        """
619 2715ade4 Sofia Papagiannaki
620 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
621 62f915a1 Antony Chazapis
             "from versions "
622 1f2d1b42 Antony Chazapis
             "where serial = ?")
623 1f2d1b42 Antony Chazapis
        self.execute(q, (serial,))
624 1f2d1b42 Antony Chazapis
        r = self.fetchone()
625 1f2d1b42 Antony Chazapis
        if r is None:
626 1f2d1b42 Antony Chazapis
            return r
627 2715ade4 Sofia Papagiannaki
628 1f2d1b42 Antony Chazapis
        if not keys:
629 1f2d1b42 Antony Chazapis
            return r
630 1f2d1b42 Antony Chazapis
        return [r[propnames[k]] for k in keys if k in propnames]
631 2715ade4 Sofia Papagiannaki
632 33b4e4a6 Antony Chazapis
    def version_put_property(self, serial, key, value):
633 33b4e4a6 Antony Chazapis
        """Set value for the property of version specified by key."""
634 2715ade4 Sofia Papagiannaki
635 33b4e4a6 Antony Chazapis
        if key not in _propnames:
636 33b4e4a6 Antony Chazapis
            return
637 33b4e4a6 Antony Chazapis
        q = "update versions set %s = ? where serial = ?" % key
638 33b4e4a6 Antony Chazapis
        self.execute(q, (value, serial))
639 2715ade4 Sofia Papagiannaki
640 44ad5860 Antony Chazapis
    def version_recluster(self, serial, cluster):
641 44ad5860 Antony Chazapis
        """Move the version into another cluster."""
642 2715ade4 Sofia Papagiannaki
643 62f915a1 Antony Chazapis
        props = self.version_get_properties(serial)
644 62f915a1 Antony Chazapis
        if not props:
645 62f915a1 Antony Chazapis
            return
646 44ad5860 Antony Chazapis
        node = props[NODE]
647 44ad5860 Antony Chazapis
        size = props[SIZE]
648 44ad5860 Antony Chazapis
        oldcluster = props[CLUSTER]
649 44ad5860 Antony Chazapis
        if cluster == oldcluster:
650 44ad5860 Antony Chazapis
            return
651 2715ade4 Sofia Papagiannaki
652 62f915a1 Antony Chazapis
        mtime = time()
653 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster)
654 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
655 2715ade4 Sofia Papagiannaki
656 62f915a1 Antony Chazapis
        q = "update versions set cluster = ? where serial = ?"
657 c915d3bf Antony Chazapis
        self.execute(q, (cluster, serial))
658 2715ade4 Sofia Papagiannaki
659 62f915a1 Antony Chazapis
    def version_remove(self, serial):
660 62f915a1 Antony Chazapis
        """Remove the serial specified."""
661 2715ade4 Sofia Papagiannaki
662 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
663 62f915a1 Antony Chazapis
        if not props:
664 62f915a1 Antony Chazapis
            return
665 62f915a1 Antony Chazapis
        node = props[NODE]
666 5161c672 Antony Chazapis
        hash = props[HASH]
667 62f915a1 Antony Chazapis
        size = props[SIZE]
668 62f915a1 Antony Chazapis
        cluster = props[CLUSTER]
669 2715ade4 Sofia Papagiannaki
670 62f915a1 Antony Chazapis
        mtime = time()
671 62f915a1 Antony Chazapis
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster)
672 2715ade4 Sofia Papagiannaki
673 62f915a1 Antony Chazapis
        q = "delete from versions where serial = ?"
674 62f915a1 Antony Chazapis
        self.execute(q, (serial,))
675 2715ade4 Sofia Papagiannaki
676 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
677 585b75e7 Sofia Papagiannaki
        if props:
678 2715ade4 Sofia Papagiannaki
            self.nodes_set_latest_version(node, props[0])
679 813e42e5 Antony Chazapis
        return hash, size
680 2715ade4 Sofia Papagiannaki
681 4819d34f Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
682 44ad5860 Antony Chazapis
        """Return a list of (key, value) pairs of the version specified by serial.
683 1f2d1b42 Antony Chazapis
           If keys is empty, return all attributes.
684 1f2d1b42 Antony Chazapis
           Othwerise, return only those specified.
685 1f2d1b42 Antony Chazapis
        """
686 2715ade4 Sofia Papagiannaki
687 1f2d1b42 Antony Chazapis
        execute = self.execute
688 1f2d1b42 Antony Chazapis
        if keys:
689 1f2d1b42 Antony Chazapis
            marks = ','.join('?' for k in keys)
690 1f2d1b42 Antony Chazapis
            q = ("select key, value from attributes "
691 4819d34f Antony Chazapis
                 "where key in (%s) and serial = ? and domain = ?" % (marks,))
692 4819d34f Antony Chazapis
            execute(q, keys + (serial, domain))
693 1f2d1b42 Antony Chazapis
        else:
694 059857e2 Antony Chazapis
            q = "select key, value from attributes where serial = ? and domain = ?"
695 059857e2 Antony Chazapis
            execute(q, (serial, domain))
696 1f2d1b42 Antony Chazapis
        return self.fetchall()
697 2715ade4 Sofia Papagiannaki
698 4819d34f Antony Chazapis
    def attribute_set(self, serial, domain, items):
699 44ad5860 Antony Chazapis
        """Set the attributes of the version specified by serial.
700 1f2d1b42 Antony Chazapis
           Receive attributes as an iterable of (key, value) pairs.
701 1f2d1b42 Antony Chazapis
        """
702 2715ade4 Sofia Papagiannaki
703 4819d34f Antony Chazapis
        q = ("insert or replace into attributes (serial, domain, key, value) "
704 4819d34f Antony Chazapis
             "values (?, ?, ?, ?)")
705 4819d34f Antony Chazapis
        self.executemany(q, ((serial, domain, k, v) for k, v in items))
706 2715ade4 Sofia Papagiannaki
707 4819d34f Antony Chazapis
    def attribute_del(self, serial, domain, keys=()):
708 44ad5860 Antony Chazapis
        """Delete attributes of the version specified by serial.
709 1f2d1b42 Antony Chazapis
           If keys is empty, delete all attributes.
710 1f2d1b42 Antony Chazapis
           Otherwise delete those specified.
711 1f2d1b42 Antony Chazapis
        """
712 2715ade4 Sofia Papagiannaki
713 1f2d1b42 Antony Chazapis
        if keys:
714 4819d34f Antony Chazapis
            q = "delete from attributes where serial = ? and domain = ? and key = ?"
715 4819d34f Antony Chazapis
            self.executemany(q, ((serial, domain, key) for key in keys))
716 1f2d1b42 Antony Chazapis
        else:
717 4819d34f Antony Chazapis
            q = "delete from attributes where serial = ? and domain = ?"
718 4819d34f Antony Chazapis
            self.execute(q, (serial, domain))
719 2715ade4 Sofia Papagiannaki
720 44ad5860 Antony Chazapis
    def attribute_copy(self, source, dest):
721 1f2d1b42 Antony Chazapis
        q = ("insert or replace into attributes "
722 4819d34f Antony Chazapis
             "select ?, domain, key, value from attributes "
723 1f2d1b42 Antony Chazapis
             "where serial = ?")
724 1f2d1b42 Antony Chazapis
        self.execute(q, (dest, source))
725 2715ade4 Sofia Papagiannaki
726 4819d34f Antony Chazapis
    def _construct_filters(self, domain, filterq):
727 4819d34f Antony Chazapis
        if not domain or not filterq:
728 60b8a083 Antony Chazapis
            return None, None
729 2715ade4 Sofia Papagiannaki
730 95d47e1a Antony Chazapis
        subqlist = []
731 95d47e1a Antony Chazapis
        append = subqlist.append
732 4819d34f Antony Chazapis
        included, excluded, opers = parse_filters(filterq)
733 95d47e1a Antony Chazapis
        args = []
734 2715ade4 Sofia Papagiannaki
735 95d47e1a Antony Chazapis
        if included:
736 c7c0790f chazapis
            subq = "exists (select 1 from attributes where serial = v.serial and domain = ? and "
737 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in included)) + ")"
738 c7c0790f chazapis
            subq += ")"
739 c7c0790f chazapis
            args += [domain]
740 95d47e1a Antony Chazapis
            args += included
741 95d47e1a Antony Chazapis
            append(subq)
742 2715ade4 Sofia Papagiannaki
743 95d47e1a Antony Chazapis
        if excluded:
744 c7c0790f chazapis
            subq = "not exists (select 1 from attributes where serial = v.serial and domain = ? and "
745 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in excluded)) + ")"
746 c7c0790f chazapis
            subq += ")"
747 c7c0790f chazapis
            args += [domain]
748 95d47e1a Antony Chazapis
            args += excluded
749 95d47e1a Antony Chazapis
            append(subq)
750 2715ade4 Sofia Papagiannaki
751 95d47e1a Antony Chazapis
        if opers:
752 95d47e1a Antony Chazapis
            for k, o, v in opers:
753 6b2f11b4 Antony Chazapis
                subq = "exists (select 1 from attributes where serial = v.serial and domain = ? and "
754 6b2f11b4 Antony Chazapis
                subq += "key = ? and value %s ?" % (o,)
755 6b2f11b4 Antony Chazapis
                subq += ")"
756 6b2f11b4 Antony Chazapis
                args += [domain, k, v]
757 6b2f11b4 Antony Chazapis
                append(subq)
758 2715ade4 Sofia Papagiannaki
759 95d47e1a Antony Chazapis
        if not subqlist:
760 95d47e1a Antony Chazapis
            return None, None
761 2715ade4 Sofia Papagiannaki
762 c7c0790f chazapis
        subq = ' and ' + ' and '.join(subqlist)
763 2715ade4 Sofia Papagiannaki
764 60b8a083 Antony Chazapis
        return subq, args
765 2715ade4 Sofia Papagiannaki
766 60b8a083 Antony Chazapis
    def _construct_paths(self, pathq):
767 60b8a083 Antony Chazapis
        if not pathq:
768 60b8a083 Antony Chazapis
            return None, None
769 2715ade4 Sofia Papagiannaki
770 d57eaad4 Antony Chazapis
        subqlist = []
771 d57eaad4 Antony Chazapis
        args = []
772 d57eaad4 Antony Chazapis
        for path, match in pathq:
773 d57eaad4 Antony Chazapis
            if match == MATCH_PREFIX:
774 d57eaad4 Antony Chazapis
                subqlist.append("n.path like ? escape '\\'")
775 d57eaad4 Antony Chazapis
                args.append(self.escape_like(path) + '%')
776 d57eaad4 Antony Chazapis
            elif match == MATCH_EXACT:
777 d57eaad4 Antony Chazapis
                subqlist.append("n.path = ?")
778 d57eaad4 Antony Chazapis
                args.append(path)
779 2715ade4 Sofia Papagiannaki
780 d57eaad4 Antony Chazapis
        subq = ' and (' + ' or '.join(subqlist) + ')'
781 d57eaad4 Antony Chazapis
        args = tuple(args)
782 2715ade4 Sofia Papagiannaki
783 60b8a083 Antony Chazapis
        return subq, args
784 2715ade4 Sofia Papagiannaki
785 7ff57991 Antony Chazapis
    def _construct_size(self, sizeq):
786 7ff57991 Antony Chazapis
        if not sizeq or len(sizeq) != 2:
787 7ff57991 Antony Chazapis
            return None, None
788 2715ade4 Sofia Papagiannaki
789 7ff57991 Antony Chazapis
        subq = ''
790 7ff57991 Antony Chazapis
        args = []
791 7ff57991 Antony Chazapis
        if sizeq[0]:
792 7ff57991 Antony Chazapis
            subq += " and v.size >= ?"
793 7ff57991 Antony Chazapis
            args += [sizeq[0]]
794 7ff57991 Antony Chazapis
        if sizeq[1]:
795 7ff57991 Antony Chazapis
            subq += " and v.size < ?"
796 7ff57991 Antony Chazapis
            args += [sizeq[1]]
797 2715ade4 Sofia Papagiannaki
798 7ff57991 Antony Chazapis
        return subq, args
799 2715ade4 Sofia Papagiannaki
800 585b75e7 Sofia Papagiannaki
    def _construct_versions_nodes_latest_version_subquery(self, before=inf):
801 585b75e7 Sofia Papagiannaki
        if before == inf:
802 585b75e7 Sofia Papagiannaki
            q = ("n.latest_version ")
803 585b75e7 Sofia Papagiannaki
            args = []
804 585b75e7 Sofia Papagiannaki
        else:
805 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
806 2715ade4 Sofia Papagiannaki
                 "from versions "
807 2715ade4 Sofia Papagiannaki
                 "where node = v.node and mtime < ?) ")
808 585b75e7 Sofia Papagiannaki
            args = [before]
809 585b75e7 Sofia Papagiannaki
        return q, args
810 2715ade4 Sofia Papagiannaki
811 585b75e7 Sofia Papagiannaki
    def _construct_latest_version_subquery(self, node=None, before=inf):
812 585b75e7 Sofia Papagiannaki
        where_cond = "node = v.node"
813 585b75e7 Sofia Papagiannaki
        args = []
814 585b75e7 Sofia Papagiannaki
        if node:
815 585b75e7 Sofia Papagiannaki
            where_cond = "node = ? "
816 585b75e7 Sofia Papagiannaki
            args = [node]
817 2715ade4 Sofia Papagiannaki
818 585b75e7 Sofia Papagiannaki
        if before == inf:
819 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
820 2715ade4 Sofia Papagiannaki
                 "from nodes "
821 2715ade4 Sofia Papagiannaki
                 "where %s) ")
822 585b75e7 Sofia Papagiannaki
        else:
823 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
824 2715ade4 Sofia Papagiannaki
                 "from versions "
825 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ?) ")
826 585b75e7 Sofia Papagiannaki
            args += [before]
827 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
828 2715ade4 Sofia Papagiannaki
829 585b75e7 Sofia Papagiannaki
    def _construct_latest_versions_subquery(self, nodes=(), before=inf):
830 585b75e7 Sofia Papagiannaki
        where_cond = ""
831 585b75e7 Sofia Papagiannaki
        args = []
832 585b75e7 Sofia Papagiannaki
        if nodes:
833 585b75e7 Sofia Papagiannaki
            where_cond = "node in (%s) " % ','.join('?' for node in nodes)
834 585b75e7 Sofia Papagiannaki
            args = nodes
835 2715ade4 Sofia Papagiannaki
836 585b75e7 Sofia Papagiannaki
        if before == inf:
837 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
838 2715ade4 Sofia Papagiannaki
                 "from nodes "
839 2715ade4 Sofia Papagiannaki
                 "where %s ) ")
840 585b75e7 Sofia Papagiannaki
        else:
841 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
842 2715ade4 Sofia Papagiannaki
                 "from versions "
843 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ? group by node) ")
844 585b75e7 Sofia Papagiannaki
            args += [before]
845 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
846 2715ade4 Sofia Papagiannaki
847 78348987 Sofia Papagiannaki
    def latest_attribute_keys(self, parent, domain, before=inf, except_cluster=0, pathq=None):
848 676edf89 Antony Chazapis
        """Return a list with all keys pairs defined
849 676edf89 Antony Chazapis
           for all latest versions under parent that
850 676edf89 Antony Chazapis
           do not belong to the cluster.
851 676edf89 Antony Chazapis
        """
852 2715ade4 Sofia Papagiannaki
853 78348987 Sofia Papagiannaki
        pathq = pathq or []
854 78348987 Sofia Papagiannaki
855 676edf89 Antony Chazapis
        # TODO: Use another table to store before=inf results.
856 60b8a083 Antony Chazapis
        q = ("select distinct a.key "
857 676edf89 Antony Chazapis
             "from attributes a, versions v, nodes n "
858 585b75e7 Sofia Papagiannaki
             "where v.serial = %s "
859 676edf89 Antony Chazapis
             "and v.cluster != ? "
860 676edf89 Antony Chazapis
             "and v.node in (select node "
861 2715ade4 Sofia Papagiannaki
             "from nodes "
862 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
863 676edf89 Antony Chazapis
             "and a.serial = v.serial "
864 4819d34f Antony Chazapis
             "and a.domain = ? "
865 676edf89 Antony Chazapis
             "and n.node = v.node")
866 2715ade4 Sofia Papagiannaki
        subq, subargs = self._construct_latest_version_subquery(
867 2715ade4 Sofia Papagiannaki
            node=None, before=before)
868 585b75e7 Sofia Papagiannaki
        args = subargs + [except_cluster, parent, domain]
869 585b75e7 Sofia Papagiannaki
        q = q % subq
870 60b8a083 Antony Chazapis
        subq, subargs = self._construct_paths(pathq)
871 60b8a083 Antony Chazapis
        if subq is not None:
872 60b8a083 Antony Chazapis
            q += subq
873 60b8a083 Antony Chazapis
            args += subargs
874 676edf89 Antony Chazapis
        self.execute(q, args)
875 676edf89 Antony Chazapis
        return [r[0] for r in self.fetchall()]
876 2715ade4 Sofia Papagiannaki
877 60b8a083 Antony Chazapis
    def latest_version_list(self, parent, prefix='', delimiter=None,
878 60b8a083 Antony Chazapis
                            start='', limit=10000, before=inf,
879 371d907a Antony Chazapis
                            except_cluster=0, pathq=[], domain=None,
880 371d907a Antony Chazapis
                            filterq=[], sizeq=None, all_props=False):
881 60b8a083 Antony Chazapis
        """Return a (list of (path, serial) tuples, list of common prefixes)
882 60b8a083 Antony Chazapis
           for the current versions of the paths with the given parent,
883 60b8a083 Antony Chazapis
           matching the following criteria.
884 2715ade4 Sofia Papagiannaki

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

888 60b8a083 Antony Chazapis
                a. parent matches
889 2715ade4 Sofia Papagiannaki

890 60b8a083 Antony Chazapis
                b. path > start
891 2715ade4 Sofia Papagiannaki

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

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

896 60b8a083 Antony Chazapis
                e. version is not in cluster
897 2715ade4 Sofia Papagiannaki

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

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

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

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

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

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

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

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

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

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

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

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

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