Statistics
| Branch: | Tag: | Revision:

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

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

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

857 60b8a083 Antony Chazapis
                a. parent matches
858 2715ade4 Sofia Papagiannaki

859 60b8a083 Antony Chazapis
                b. path > start
860 2715ade4 Sofia Papagiannaki

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

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

865 60b8a083 Antony Chazapis
                e. version is not in cluster
866 2715ade4 Sofia Papagiannaki

867 60b8a083 Antony Chazapis
                f. the path does not have the delimiter occuring
868 60b8a083 Antony Chazapis
                   after the prefix, or ends with the delimiter
869 2715ade4 Sofia Papagiannaki

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

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

875 60b8a083 Antony Chazapis
                   key
876 60b8a083 Antony Chazapis
                       an attribute with this key must exist
877 2715ade4 Sofia Papagiannaki

878 60b8a083 Antony Chazapis
                   !key
879 60b8a083 Antony Chazapis
                       an attribute with this key must not exist
880 2715ade4 Sofia Papagiannaki

881 60b8a083 Antony Chazapis
                   key ?op value
882 60b8a083 Antony Chazapis
                       the attribute with this key satisfies the value
883 1713c946 Antony Chazapis
                       where ?op is one of =, != <=, >=, <, >.
884 2715ade4 Sofia Papagiannaki

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

887 60b8a083 Antony Chazapis
           The list of common prefixes includes the prefixes
888 60b8a083 Antony Chazapis
           matching up to the first delimiter after prefix,
889 60b8a083 Antony Chazapis
           and are reported only once, as "virtual directories".
890 60b8a083 Antony Chazapis
           The delimiter is included in the prefixes.
891 2715ade4 Sofia Papagiannaki

892 60b8a083 Antony Chazapis
           If arguments are None, then the corresponding matching rule
893 60b8a083 Antony Chazapis
           will always match.
894 2715ade4 Sofia Papagiannaki

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

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

989 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
990 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
991 2bbf1544 Georgios D. Tsoukalas

992 2bbf1544 Georgios D. Tsoukalas
        """
993 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
994 2bbf1544 Georgios D. Tsoukalas
            cluster_where = "and cluster = ?"
995 2bbf1544 Georgios D. Tsoukalas
            args = (uuid, int(cluster))
996 2bbf1544 Georgios D. Tsoukalas
        else:
997 2bbf1544 Georgios D. Tsoukalas
            cluster_where = ""
998 2bbf1544 Georgios D. Tsoukalas
            args = (uuid,)
999 2715ade4 Sofia Papagiannaki
1000 37bee317 Antony Chazapis
        q = ("select n.path, v.serial "
1001 37bee317 Antony Chazapis
             "from versions v, nodes n "
1002 37bee317 Antony Chazapis
             "where v.serial = (select max(serial) "
1003 2715ade4 Sofia Papagiannaki
             "from versions "
1004 2bbf1544 Georgios D. Tsoukalas
             "where uuid = ? %s) "
1005 2bbf1544 Georgios D. Tsoukalas
             "and n.node = v.node") % cluster_where
1006 2bbf1544 Georgios D. Tsoukalas
        self.execute(q, args)
1007 37bee317 Antony Chazapis
        return self.fetchone()
1008 5576e6dd Sofia Papagiannaki
1009 5576e6dd Sofia Papagiannaki
    def domain_object_list(self, domain, cluster=None):
1010 5576e6dd Sofia Papagiannaki
        """Return a list of (path, property list, attribute dictionary)
1011 5576e6dd Sofia Papagiannaki
           for the objects in the specific domain and cluster.
1012 5576e6dd Sofia Papagiannaki
        """
1013 5576e6dd Sofia Papagiannaki
1014 5576e6dd Sofia Papagiannaki
        q = ("select n.path, v.serial, v.node, v.hash, "
1015 5576e6dd Sofia Papagiannaki
             "v.size, v.type, v.source, v.mtime, v.muser, "
1016 5576e6dd Sofia Papagiannaki
             "v.uuid, v.checksum, v.cluster, a.key, a.value "
1017 5576e6dd Sofia Papagiannaki
             "from nodes n, versions v, attributes a "
1018 5576e6dd Sofia Papagiannaki
             "where n.node = v.node and "
1019 5576e6dd Sofia Papagiannaki
             "n.latest_version = v.serial and "
1020 5576e6dd Sofia Papagiannaki
             "v.serial = a.serial and "
1021 5576e6dd Sofia Papagiannaki
             "a.domain = ? ")
1022 5576e6dd Sofia Papagiannaki
        args = [domain]
1023 5576e6dd Sofia Papagiannaki
        if cluster != None:
1024 5576e6dd Sofia Papagiannaki
            q += "and v.cluster = ?"
1025 5576e6dd Sofia Papagiannaki
            args += [cluster]
1026 5576e6dd Sofia Papagiannaki
1027 5576e6dd Sofia Papagiannaki
        self.execute(q, args)
1028 5576e6dd Sofia Papagiannaki
        rows = self.fetchall()
1029 5576e6dd Sofia Papagiannaki
1030 5576e6dd Sofia Papagiannaki
        group_by = itemgetter(slice(12))
1031 5576e6dd Sofia Papagiannaki
        rows.sort(key = group_by)
1032 5576e6dd Sofia Papagiannaki
        groups = groupby(rows, group_by)
1033 5576e6dd Sofia Papagiannaki
        return [(k[0], k[1:], dict([i[12:] for i in data])) \
1034 5576e6dd Sofia Papagiannaki
            for (k, data) in groups]