Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (39.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 0534576c Sofia Papagiannaki
                          ( serial      integer,
177 0534576c Sofia Papagiannaki
                            domain      text,
178 0534576c Sofia Papagiannaki
                            key         text,
179 0534576c Sofia Papagiannaki
                            value       text,
180 0534576c Sofia Papagiannaki
                            node        integer not null    default 0,
181 0534576c Sofia Papagiannaki
                            is_latest   boolean not null    default 1,
182 4819d34f Antony Chazapis
                            primary key (serial, domain, key)
183 1f2d1b42 Antony Chazapis
                            foreign key (serial)
184 1f2d1b42 Antony Chazapis
                            references versions(serial)
185 1f2d1b42 Antony Chazapis
                            on update cascade
186 1f2d1b42 Antony Chazapis
                            on delete cascade ) """)
187 5576e6dd Sofia Papagiannaki
        execute(""" create index if not exists idx_attributes_domain
188 5576e6dd Sofia Papagiannaki
                    on attributes(domain) """)
189 0534576c Sofia Papagiannaki
        execute(""" create index if not exists idx_attributes_serial_node
190 0534576c Sofia Papagiannaki
                    on attributes(serial, node) """)
191 2715ade4 Sofia Papagiannaki
192 cf53943b Sofia Papagiannaki
        wrapper = self.wrapper
193 cf53943b Sofia Papagiannaki
        wrapper.execute()
194 cf53943b Sofia Papagiannaki
        try:
195 cf53943b Sofia Papagiannaki
            q = "insert or ignore into nodes(node, parent) values (?, ?)"
196 cf53943b Sofia Papagiannaki
            execute(q, (ROOTNODE, ROOTNODE))
197 cf53943b Sofia Papagiannaki
        finally:
198 cf53943b Sofia Papagiannaki
            wrapper.commit()
199 2715ade4 Sofia Papagiannaki
200 44ad5860 Antony Chazapis
    def node_create(self, parent, path):
201 44ad5860 Antony Chazapis
        """Create a new node from the given properties.
202 44ad5860 Antony Chazapis
           Return the node identifier of the new node.
203 44ad5860 Antony Chazapis
        """
204 2715ade4 Sofia Papagiannaki
205 44ad5860 Antony Chazapis
        q = ("insert into nodes (parent, path) "
206 44ad5860 Antony Chazapis
             "values (?, ?)")
207 44ad5860 Antony Chazapis
        props = (parent, path)
208 44ad5860 Antony Chazapis
        return self.execute(q, props).lastrowid
209 2715ade4 Sofia Papagiannaki
210 0534576c Sofia Papagiannaki
    def node_lookup(self, path, for_update=False):
211 44ad5860 Antony Chazapis
        """Lookup the current node of the given path.
212 44ad5860 Antony Chazapis
           Return None if the path is not found.
213 985b9b09 Sofia Papagiannaki

214 985b9b09 Sofia Papagiannaki
           kwargs is not used: it is passed for conformance
215 44ad5860 Antony Chazapis
        """
216 2715ade4 Sofia Papagiannaki
217 c1cdc455 Antony Chazapis
        q = "select node from nodes where path = ?"
218 44ad5860 Antony Chazapis
        self.execute(q, (path,))
219 44ad5860 Antony Chazapis
        r = self.fetchone()
220 44ad5860 Antony Chazapis
        if r is not None:
221 44ad5860 Antony Chazapis
            return r[0]
222 44ad5860 Antony Chazapis
        return None
223 2715ade4 Sofia Papagiannaki
224 4d15c94e Sofia Papagiannaki
    def node_lookup_bulk(self, paths):
225 2715ade4 Sofia Papagiannaki
        """Lookup the current nodes for the given paths.
226 4d15c94e Sofia Papagiannaki
           Return () if the path is not found.
227 4d15c94e Sofia Papagiannaki
        """
228 2715ade4 Sofia Papagiannaki
229 4d15c94e Sofia Papagiannaki
        placeholders = ','.join('?' for path in paths)
230 cf4a7a7b Sofia Papagiannaki
        q = "select node from nodes where path in (%s)" % placeholders
231 4d15c94e Sofia Papagiannaki
        self.execute(q, paths)
232 cf4a7a7b Sofia Papagiannaki
        r = self.fetchall()
233 cf4a7a7b Sofia Papagiannaki
        if r is not None:
234 2715ade4 Sofia Papagiannaki
            return [row[0] for row in r]
235 cf4a7a7b Sofia Papagiannaki
        return None
236 2715ade4 Sofia Papagiannaki
237 62f915a1 Antony Chazapis
    def node_get_properties(self, node):
238 62f915a1 Antony Chazapis
        """Return the node's (parent, path).
239 62f915a1 Antony Chazapis
           Return None if the node is not found.
240 62f915a1 Antony Chazapis
        """
241 2715ade4 Sofia Papagiannaki
242 62f915a1 Antony Chazapis
        q = "select parent, path from nodes where node = ?"
243 c1cdc455 Antony Chazapis
        self.execute(q, (node,))
244 62f915a1 Antony Chazapis
        return self.fetchone()
245 2715ade4 Sofia Papagiannaki
246 676edf89 Antony Chazapis
    def node_get_versions(self, node, keys=(), propnames=_propnames):
247 676edf89 Antony Chazapis
        """Return the properties of all versions at node.
248 676edf89 Antony Chazapis
           If keys is empty, return all properties in the order
249 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
250 29148653 Sofia Papagiannaki
            checksum, cluster).
251 676edf89 Antony Chazapis
        """
252 2715ade4 Sofia Papagiannaki
253 29148653 Sofia Papagiannaki
        q = ("select serial, node, hash, size, type, source, mtime, muser, "
254 29148653 Sofia Papagiannaki
             "uuid, checksum, cluster "
255 676edf89 Antony Chazapis
             "from versions "
256 676edf89 Antony Chazapis
             "where node = ?")
257 60b8a083 Antony Chazapis
        self.execute(q, (node,))
258 676edf89 Antony Chazapis
        r = self.fetchall()
259 676edf89 Antony Chazapis
        if r is None:
260 676edf89 Antony Chazapis
            return r
261 2715ade4 Sofia Papagiannaki
262 676edf89 Antony Chazapis
        if not keys:
263 676edf89 Antony Chazapis
            return r
264 676edf89 Antony Chazapis
        return [[p[propnames[k]] for k in keys if k in propnames] for p in r]
265 2715ade4 Sofia Papagiannaki
266 c915d3bf Antony Chazapis
    def node_count_children(self, node):
267 44ad5860 Antony Chazapis
        """Return node's child count."""
268 2715ade4 Sofia Papagiannaki
269 c915d3bf Antony Chazapis
        q = "select count(node) from nodes where parent = ? and node != 0"
270 44ad5860 Antony Chazapis
        self.execute(q, (node,))
271 62f915a1 Antony Chazapis
        r = self.fetchone()
272 44ad5860 Antony Chazapis
        if r is None:
273 44ad5860 Antony Chazapis
            return 0
274 62f915a1 Antony Chazapis
        return r[0]
275 2715ade4 Sofia Papagiannaki
276 0534576c Sofia Papagiannaki
    def node_purge_children(self, parent, before=inf, cluster=0,
277 0534576c Sofia Papagiannaki
                            update_statistics_ancestors_depth=None):
278 c915d3bf Antony Chazapis
        """Delete all versions with the specified
279 c915d3bf Antony Chazapis
           parent and cluster, and return
280 0a92ff85 Sofia Papagiannaki
           the hashes, the size and the serials of versions deleted.
281 c915d3bf Antony Chazapis
           Clears out nodes with no remaining versions.
282 c915d3bf Antony Chazapis
        """
283 2715ade4 Sofia Papagiannaki
284 c915d3bf Antony Chazapis
        execute = self.execute
285 c915d3bf Antony Chazapis
        q = ("select count(serial), sum(size) from versions "
286 c915d3bf Antony Chazapis
             "where node in (select node "
287 2715ade4 Sofia Papagiannaki
             "from nodes "
288 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
289 c915d3bf Antony Chazapis
             "and cluster = ? "
290 c915d3bf Antony Chazapis
             "and mtime <= ?")
291 c915d3bf Antony Chazapis
        args = (parent, cluster, before)
292 c915d3bf Antony Chazapis
        execute(q, args)
293 c915d3bf Antony Chazapis
        nr, size = self.fetchone()
294 c915d3bf Antony Chazapis
        if not nr:
295 442bc80c Sofia Papagiannaki
            return (), 0, ()
296 60b8a083 Antony Chazapis
        mtime = time()
297 60b8a083 Antony Chazapis
        self.statistics_update(parent, -nr, -size, mtime, cluster)
298 0534576c Sofia Papagiannaki
        self.statistics_update_ancestors(parent, -nr, -size, mtime, cluster,
299 0534576c Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
300 2715ade4 Sofia Papagiannaki
301 5576e6dd Sofia Papagiannaki
        q = ("select hash, serial from versions "
302 c915d3bf Antony Chazapis
             "where node in (select node "
303 2715ade4 Sofia Papagiannaki
             "from nodes "
304 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
305 c915d3bf Antony Chazapis
             "and cluster = ? "
306 c915d3bf Antony Chazapis
             "and mtime <= ?")
307 c915d3bf Antony Chazapis
        execute(q, args)
308 388ea25f Sofia Papagiannaki
        hashes = []
309 388ea25f Sofia Papagiannaki
        serials = []
310 388ea25f Sofia Papagiannaki
        for r in self.fetchall():
311 388ea25f Sofia Papagiannaki
            hashes += [r[0]]
312 388ea25f Sofia Papagiannaki
            serials += [r[1]]
313 0534576c Sofia Papagiannaki
314 c915d3bf Antony Chazapis
        q = ("delete from versions "
315 c915d3bf Antony Chazapis
             "where node in (select node "
316 2715ade4 Sofia Papagiannaki
             "from nodes "
317 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
318 c915d3bf Antony Chazapis
             "and cluster = ? "
319 c915d3bf Antony Chazapis
             "and mtime <= ?")
320 c915d3bf Antony Chazapis
        execute(q, args)
321 62f915a1 Antony Chazapis
        q = ("delete from nodes "
322 62f915a1 Antony Chazapis
             "where node in (select node from nodes n "
323 2715ade4 Sofia Papagiannaki
             "where (select count(serial) "
324 2715ade4 Sofia Papagiannaki
             "from versions "
325 2715ade4 Sofia Papagiannaki
             "where node = n.node) = 0 "
326 2715ade4 Sofia Papagiannaki
             "and parent = ?)")
327 62f915a1 Antony Chazapis
        execute(q, (parent,))
328 388ea25f Sofia Papagiannaki
        return hashes, size, serials
329 2715ade4 Sofia Papagiannaki
330 0534576c Sofia Papagiannaki
    def node_purge(self, node, before=inf, cluster=0,
331 0534576c Sofia Papagiannaki
                   update_statistics_ancestors_depth=None):
332 c915d3bf Antony Chazapis
        """Delete all versions with the specified
333 c915d3bf Antony Chazapis
           node and cluster, and return
334 0a92ff85 Sofia Papagiannaki
           the hashes, the size and the serials of versions deleted.
335 c915d3bf Antony Chazapis
           Clears out the node if it has no remaining versions.
336 c915d3bf Antony Chazapis
        """
337 2715ade4 Sofia Papagiannaki
338 c915d3bf Antony Chazapis
        execute = self.execute
339 c915d3bf Antony Chazapis
        q = ("select count(serial), sum(size) from versions "
340 c915d3bf Antony Chazapis
             "where node = ? "
341 c915d3bf Antony Chazapis
             "and cluster = ? "
342 c915d3bf Antony Chazapis
             "and mtime <= ?")
343 c915d3bf Antony Chazapis
        args = (node, cluster, before)
344 c915d3bf Antony Chazapis
        execute(q, args)
345 c915d3bf Antony Chazapis
        nr, size = self.fetchone()
346 c915d3bf Antony Chazapis
        if not nr:
347 442bc80c Sofia Papagiannaki
            return (), 0, ()
348 60b8a083 Antony Chazapis
        mtime = time()
349 0534576c Sofia Papagiannaki
        self.statistics_update_ancestors(node, -nr, -size, mtime, cluster,
350 0534576c Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
351 2715ade4 Sofia Papagiannaki
352 5576e6dd Sofia Papagiannaki
        q = ("select hash, serial from versions "
353 c915d3bf Antony Chazapis
             "where node = ? "
354 c915d3bf Antony Chazapis
             "and cluster = ? "
355 c915d3bf Antony Chazapis
             "and mtime <= ?")
356 c915d3bf Antony Chazapis
        execute(q, args)
357 388ea25f Sofia Papagiannaki
        hashes = []
358 388ea25f Sofia Papagiannaki
        serials = []
359 388ea25f Sofia Papagiannaki
        for r in self.fetchall():
360 388ea25f Sofia Papagiannaki
            hashes += [r[0]]
361 388ea25f Sofia Papagiannaki
            serials += [r[1]]
362 5576e6dd Sofia Papagiannaki
363 c915d3bf Antony Chazapis
        q = ("delete from versions "
364 c915d3bf Antony Chazapis
             "where node = ? "
365 c915d3bf Antony Chazapis
             "and cluster = ? "
366 c915d3bf Antony Chazapis
             "and mtime <= ?")
367 c915d3bf Antony Chazapis
        execute(q, args)
368 62f915a1 Antony Chazapis
        q = ("delete from nodes "
369 62f915a1 Antony Chazapis
             "where node in (select node from nodes n "
370 2715ade4 Sofia Papagiannaki
             "where (select count(serial) "
371 2715ade4 Sofia Papagiannaki
             "from versions "
372 2715ade4 Sofia Papagiannaki
             "where node = n.node) = 0 "
373 2715ade4 Sofia Papagiannaki
             "and node = ?)")
374 62f915a1 Antony Chazapis
        execute(q, (node,))
375 388ea25f Sofia Papagiannaki
        return hashes, size, serials
376 2715ade4 Sofia Papagiannaki
377 0534576c Sofia Papagiannaki
    def node_remove(self, node, update_statistics_ancestors_depth=None):
378 c915d3bf Antony Chazapis
        """Remove the node specified.
379 c915d3bf Antony Chazapis
           Return false if the node has children or is not found.
380 c915d3bf Antony Chazapis
        """
381 2715ade4 Sofia Papagiannaki
382 c1cdc455 Antony Chazapis
        if self.node_count_children(node):
383 c915d3bf Antony Chazapis
            return False
384 2715ade4 Sofia Papagiannaki
385 c915d3bf Antony Chazapis
        mtime = time()
386 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), cluster "
387 c1cdc455 Antony Chazapis
             "from versions "
388 c1cdc455 Antony Chazapis
             "where node = ? "
389 c1cdc455 Antony Chazapis
             "group by cluster")
390 c915d3bf Antony Chazapis
        self.execute(q, (node,))
391 c915d3bf Antony Chazapis
        for population, size, cluster in self.fetchall():
392 2715ade4 Sofia Papagiannaki
            self.statistics_update_ancestors(
393 0534576c Sofia Papagiannaki
                node, -population, -size, mtime, cluster,
394 0534576c Sofia Papagiannaki
                update_statistics_ancestors_depth)
395 2715ade4 Sofia Papagiannaki
396 c915d3bf Antony Chazapis
        q = "delete from nodes where node = ?"
397 c915d3bf Antony Chazapis
        self.execute(q, (node,))
398 c915d3bf Antony Chazapis
        return True
399 2715ade4 Sofia Papagiannaki
400 d1e7d2b4 Sofia Papagiannaki
    def node_accounts(self, accounts=()):
401 ae6199c5 Sofia Papagiannaki
        q = ("select path, node from nodes where node != 0 and parent = 0 ")
402 d1e7d2b4 Sofia Papagiannaki
        args = []
403 d1e7d2b4 Sofia Papagiannaki
        if accounts:
404 d1e7d2b4 Sofia Papagiannaki
            placeholders = ','.join('?' for a in accounts)
405 d1e7d2b4 Sofia Papagiannaki
            q += ("and path in (%s)" % placeholders)
406 d1e7d2b4 Sofia Papagiannaki
            args += accounts
407 d1e7d2b4 Sofia Papagiannaki
        return self.execute(q, args).fetchall()
408 d1e7d2b4 Sofia Papagiannaki
409 ae6199c5 Sofia Papagiannaki
    def node_account_quotas(self):
410 ae6199c5 Sofia Papagiannaki
        q = ("select n.path, p.value from nodes n, policy p "
411 ae6199c5 Sofia Papagiannaki
             "where n.node != 0 and n.parent = 0 "
412 29148653 Sofia Papagiannaki
             "and n.node = p.node and p.key = 'quota'")
413 ae6199c5 Sofia Papagiannaki
        return dict(self.execute(q).fetchall())
414 ae6199c5 Sofia Papagiannaki
415 2ff02341 Sofia Papagiannaki
    def node_account_usage(self, account=None, cluster=0):
416 2ff02341 Sofia Papagiannaki
        """Return usage for a specific account.
417 d1e7d2b4 Sofia Papagiannaki

418 2ff02341 Sofia Papagiannaki
        Keyword arguments:
419 2ff02341 Sofia Papagiannaki
        account -- (default None: list usage for all the accounts)
420 2ff02341 Sofia Papagiannaki
        cluster -- list current, history or deleted usage (default 0: normal)
421 2ff02341 Sofia Papagiannaki
        """
422 d1e7d2b4 Sofia Papagiannaki
423 2ff02341 Sofia Papagiannaki
        q = ("select n3.path, sum(v.size) from "
424 2ff02341 Sofia Papagiannaki
             "versions v, nodes n1, nodes n2, nodes n3 "
425 2ff02341 Sofia Papagiannaki
             "where v.node = n1.node "
426 2ff02341 Sofia Papagiannaki
             "and v.cluster = ? "
427 2ff02341 Sofia Papagiannaki
             "and n1.parent = n2.node "
428 2ff02341 Sofia Papagiannaki
             "and n2.parent = n3.node "
429 2ff02341 Sofia Papagiannaki
             "and n3.parent = 0 "
430 2ff02341 Sofia Papagiannaki
             "and n3.node != 0 ")
431 2ff02341 Sofia Papagiannaki
        args = [cluster]
432 2ff02341 Sofia Papagiannaki
        if account:
433 2ff02341 Sofia Papagiannaki
            q += ("and n3.path = ? ")
434 2ff02341 Sofia Papagiannaki
            args += [account]
435 2ff02341 Sofia Papagiannaki
        q += ("group by n3.path")
436 2ff02341 Sofia Papagiannaki
437 2ff02341 Sofia Papagiannaki
        print '###', q, args
438 d1e7d2b4 Sofia Papagiannaki
        self.execute(q, args)
439 2ff02341 Sofia Papagiannaki
        return dict(self.fetchall())
440 d1e7d2b4 Sofia Papagiannaki
441 5e7485da Antony Chazapis
    def policy_get(self, node):
442 5e7485da Antony Chazapis
        q = "select key, value from policy where node = ?"
443 5e7485da Antony Chazapis
        self.execute(q, (node,))
444 5e7485da Antony Chazapis
        return dict(self.fetchall())
445 2715ade4 Sofia Papagiannaki
446 5e7485da Antony Chazapis
    def policy_set(self, node, policy):
447 5e7485da Antony Chazapis
        q = "insert or replace into policy (node, key, value) values (?, ?, ?)"
448 5e7485da Antony Chazapis
        self.executemany(q, ((node, k, v) for k, v in policy.iteritems()))
449 2715ade4 Sofia Papagiannaki
450 c1cdc455 Antony Chazapis
    def statistics_get(self, node, cluster=0):
451 c1cdc455 Antony Chazapis
        """Return population, total size and last mtime
452 c1cdc455 Antony Chazapis
           for all versions under node that belong to the cluster.
453 c1cdc455 Antony Chazapis
        """
454 2715ade4 Sofia Papagiannaki
455 62f915a1 Antony Chazapis
        q = ("select population, size, mtime from statistics "
456 c1cdc455 Antony Chazapis
             "where node = ? and cluster = ?")
457 c1cdc455 Antony Chazapis
        self.execute(q, (node, cluster))
458 62f915a1 Antony Chazapis
        return self.fetchone()
459 2715ade4 Sofia Papagiannaki
460 c1cdc455 Antony Chazapis
    def statistics_update(self, node, population, size, mtime, cluster=0):
461 c1cdc455 Antony Chazapis
        """Update the statistics of the given node.
462 c1cdc455 Antony Chazapis
           Statistics keep track the population, total
463 c1cdc455 Antony Chazapis
           size of objects and mtime in the node's namespace.
464 c1cdc455 Antony Chazapis
           May be zero or positive or negative numbers.
465 c1cdc455 Antony Chazapis
        """
466 2715ade4 Sofia Papagiannaki
467 62f915a1 Antony Chazapis
        qs = ("select population, size from statistics "
468 c1cdc455 Antony Chazapis
              "where node = ? and cluster = ?")
469 29148653 Sofia Papagiannaki
        qu = ("insert or replace into statistics "
470 29148653 Sofia Papagiannaki
              "(node, population, size, mtime, cluster) "
471 c1cdc455 Antony Chazapis
              "values (?, ?, ?, ?, ?)")
472 c1cdc455 Antony Chazapis
        self.execute(qs, (node, cluster))
473 c1cdc455 Antony Chazapis
        r = self.fetchone()
474 c1cdc455 Antony Chazapis
        if r is None:
475 c1cdc455 Antony Chazapis
            prepopulation, presize = (0, 0)
476 c1cdc455 Antony Chazapis
        else:
477 c1cdc455 Antony Chazapis
            prepopulation, presize = r
478 c1cdc455 Antony Chazapis
        population += prepopulation
479 096a7c3b Sofia Papagiannaki
        population = max(population, 0)
480 c1cdc455 Antony Chazapis
        size += presize
481 c1cdc455 Antony Chazapis
        self.execute(qu, (node, population, size, mtime, cluster))
482 2715ade4 Sofia Papagiannaki
483 0534576c Sofia Papagiannaki
    def statistics_update_ancestors(self, node, population, size, mtime,
484 0534576c Sofia Papagiannaki
                                    cluster=0, recursion_depth=None):
485 c1cdc455 Antony Chazapis
        """Update the statistics of the given node's parent.
486 c1cdc455 Antony Chazapis
           Then recursively update all parents up to the root.
487 c1cdc455 Antony Chazapis
           Population is not recursive.
488 c1cdc455 Antony Chazapis
        """
489 2715ade4 Sofia Papagiannaki
490 0534576c Sofia Papagiannaki
        i = 0
491 c1cdc455 Antony Chazapis
        while True:
492 0534576c Sofia Papagiannaki
            if node == ROOTNODE:
493 0534576c Sofia Papagiannaki
                break
494 0534576c Sofia Papagiannaki
            if recursion_depth and recursion_depth <= i:
495 c1cdc455 Antony Chazapis
                break
496 62f915a1 Antony Chazapis
            props = self.node_get_properties(node)
497 62f915a1 Antony Chazapis
            if props is None:
498 c1cdc455 Antony Chazapis
                break
499 62f915a1 Antony Chazapis
            parent, path = props
500 62f915a1 Antony Chazapis
            self.statistics_update(parent, population, size, mtime, cluster)
501 62f915a1 Antony Chazapis
            node = parent
502 2715ade4 Sofia Papagiannaki
            population = 0  # Population isn't recursive
503 0534576c Sofia Papagiannaki
            i += 1
504 2715ade4 Sofia Papagiannaki
505 62f915a1 Antony Chazapis
    def statistics_latest(self, node, before=inf, except_cluster=0):
506 62f915a1 Antony Chazapis
        """Return population, total size and last mtime
507 62f915a1 Antony Chazapis
           for all latest versions under node that
508 62f915a1 Antony Chazapis
           do not belong to the cluster.
509 62f915a1 Antony Chazapis
        """
510 2715ade4 Sofia Papagiannaki
511 676edf89 Antony Chazapis
        execute = self.execute
512 676edf89 Antony Chazapis
        fetchone = self.fetchone
513 2715ade4 Sofia Papagiannaki
514 62f915a1 Antony Chazapis
        # The node.
515 62f915a1 Antony Chazapis
        props = self.node_get_properties(node)
516 62f915a1 Antony Chazapis
        if props is None:
517 62f915a1 Antony Chazapis
            return None
518 62f915a1 Antony Chazapis
        parent, path = props
519 2715ade4 Sofia Papagiannaki
520 62f915a1 Antony Chazapis
        # The latest version.
521 29148653 Sofia Papagiannaki
        q = ("select serial, node, hash, size, type, source, mtime, muser, "
522 29148653 Sofia Papagiannaki
             "uuid, checksum, cluster "
523 585b75e7 Sofia Papagiannaki
             "from versions v "
524 585b75e7 Sofia Papagiannaki
             "where serial = %s "
525 62f915a1 Antony Chazapis
             "and cluster != ?")
526 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
527 2715ade4 Sofia Papagiannaki
            node=node, before=before)
528 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster])
529 676edf89 Antony Chazapis
        props = fetchone()
530 62f915a1 Antony Chazapis
        if props is None:
531 62f915a1 Antony Chazapis
            return None
532 62f915a1 Antony Chazapis
        mtime = props[MTIME]
533 2715ade4 Sofia Papagiannaki
534 62f915a1 Antony Chazapis
        # First level, just under node (get population).
535 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
536 62f915a1 Antony Chazapis
             "from versions v "
537 585b75e7 Sofia Papagiannaki
             "where serial = %s "
538 62f915a1 Antony Chazapis
             "and cluster != ? "
539 62f915a1 Antony Chazapis
             "and node in (select node "
540 2715ade4 Sofia Papagiannaki
             "from nodes "
541 2715ade4 Sofia Papagiannaki
             "where parent = ?)")
542 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
543 2715ade4 Sofia Papagiannaki
            node=None, before=before)
544 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster, node])
545 62f915a1 Antony Chazapis
        r = fetchone()
546 62f915a1 Antony Chazapis
        if r is None:
547 62f915a1 Antony Chazapis
            return None
548 62f915a1 Antony Chazapis
        count = r[0]
549 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
550 62f915a1 Antony Chazapis
        if count == 0:
551 62f915a1 Antony Chazapis
            return (0, 0, mtime)
552 2715ade4 Sofia Papagiannaki
553 62f915a1 Antony Chazapis
        # All children (get size and mtime).
554 92da0e5a Antony Chazapis
        # This is why the full path is stored.
555 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
556 62f915a1 Antony Chazapis
             "from versions v "
557 585b75e7 Sofia Papagiannaki
             "where serial = %s "
558 62f915a1 Antony Chazapis
             "and cluster != ? "
559 62f915a1 Antony Chazapis
             "and node in (select node "
560 2715ade4 Sofia Papagiannaki
             "from nodes "
561 2715ade4 Sofia Papagiannaki
             "where path like ? escape '\\')")
562 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
563 2715ade4 Sofia Papagiannaki
            node=None, before=before)
564 2715ade4 Sofia Papagiannaki
        execute(
565 2715ade4 Sofia Papagiannaki
            q % subq, args + [except_cluster, self.escape_like(path) + '%'])
566 62f915a1 Antony Chazapis
        r = fetchone()
567 62f915a1 Antony Chazapis
        if r is None:
568 62f915a1 Antony Chazapis
            return None
569 62f915a1 Antony Chazapis
        size = r[1] - props[SIZE]
570 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
571 62f915a1 Antony Chazapis
        return (count, size, mtime)
572 2715ade4 Sofia Papagiannaki
573 585b75e7 Sofia Papagiannaki
    def nodes_set_latest_version(self, node, serial):
574 2715ade4 Sofia Papagiannaki
        q = ("update nodes set latest_version = ? where node = ?")
575 585b75e7 Sofia Papagiannaki
        props = (serial, node)
576 585b75e7 Sofia Papagiannaki
        self.execute(q, props)
577 2715ade4 Sofia Papagiannaki
578 0534576c Sofia Papagiannaki
    def version_create(self, node, hash, size, type, source, muser, uuid,
579 0534576c Sofia Papagiannaki
                       checksum, cluster=0,
580 0534576c Sofia Papagiannaki
                       update_statistics_ancestors_depth=None):
581 44ad5860 Antony Chazapis
        """Create a new version from the given properties.
582 44ad5860 Antony Chazapis
           Return the (serial, mtime) of the new version.
583 44ad5860 Antony Chazapis
        """
584 2715ade4 Sofia Papagiannaki
585 29148653 Sofia Papagiannaki
        q = ("insert into versions (node, hash, size, type, source, mtime, "
586 29148653 Sofia Papagiannaki
             "muser, uuid, checksum, cluster) "
587 33b4e4a6 Antony Chazapis
             "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
588 44ad5860 Antony Chazapis
        mtime = time()
589 2715ade4 Sofia Papagiannaki
        props = (node, hash, size, type, source, mtime, muser,
590 2715ade4 Sofia Papagiannaki
                 uuid, checksum, cluster)
591 44ad5860 Antony Chazapis
        serial = self.execute(q, props).lastrowid
592 0534576c Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster,
593 0534576c Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
594 2715ade4 Sofia Papagiannaki
595 585b75e7 Sofia Papagiannaki
        self.nodes_set_latest_version(node, serial)
596 2715ade4 Sofia Papagiannaki
597 44ad5860 Antony Chazapis
        return serial, mtime
598 2715ade4 Sofia Papagiannaki
599 cf4a7a7b Sofia Papagiannaki
    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
600 44ad5860 Antony Chazapis
        """Lookup the current version of the given node.
601 44ad5860 Antony Chazapis
           Return a list with its properties:
602 2715ade4 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime,
603 2715ade4 Sofia Papagiannaki
            muser, uuid, checksum, cluster)
604 44ad5860 Antony Chazapis
           or None if the current version is not found in the given cluster.
605 44ad5860 Antony Chazapis
        """
606 2715ade4 Sofia Papagiannaki
607 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
608 585b75e7 Sofia Papagiannaki
             "from versions v "
609 585b75e7 Sofia Papagiannaki
             "where serial = %s "
610 44ad5860 Antony Chazapis
             "and cluster = ?")
611 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
612 2715ade4 Sofia Papagiannaki
            node=node, before=before)
613 cf4a7a7b Sofia Papagiannaki
        if not all_props:
614 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq)
615 cf4a7a7b Sofia Papagiannaki
        else:
616 29148653 Sofia Papagiannaki
            q = q % (("serial, node, hash, size, type, source, mtime, muser, "
617 29148653 Sofia Papagiannaki
                      "uuid, checksum, cluster"),
618 29148653 Sofia Papagiannaki
                     subq)
619 2715ade4 Sofia Papagiannaki
620 585b75e7 Sofia Papagiannaki
        self.execute(q, args + [cluster])
621 44ad5860 Antony Chazapis
        props = self.fetchone()
622 44ad5860 Antony Chazapis
        if props is not None:
623 44ad5860 Antony Chazapis
            return props
624 44ad5860 Antony Chazapis
        return None
625 4d15c94e Sofia Papagiannaki
626 29148653 Sofia Papagiannaki
    def version_lookup_bulk(self, nodes, before=inf, cluster=0,
627 ba402621 Sofia Papagiannaki
                            all_props=True, order_by_path=False):
628 4d15c94e Sofia Papagiannaki
        """Lookup the current versions of the given nodes.
629 4d15c94e Sofia Papagiannaki
           Return a list with their properties:
630 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
631 29148653 Sofia Papagiannaki
            checksum, cluster).
632 4d15c94e Sofia Papagiannaki
        """
633 2715ade4 Sofia Papagiannaki
634 585b75e7 Sofia Papagiannaki
        if not nodes:
635 2715ade4 Sofia Papagiannaki
            return ()
636 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
637 ba402621 Sofia Papagiannaki
             "from versions v, nodes n "
638 585b75e7 Sofia Papagiannaki
             "where serial in %s "
639 ba402621 Sofia Papagiannaki
             "and v.node = n.node "
640 ba402621 Sofia Papagiannaki
             "and cluster = ? %s ")
641 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_versions_subquery(
642 2715ade4 Sofia Papagiannaki
            nodes=nodes, before=before)
643 cf4a7a7b Sofia Papagiannaki
        if not all_props:
644 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq, '')
645 cf4a7a7b Sofia Papagiannaki
        else:
646 ba402621 Sofia Papagiannaki
            q = q % (("serial, v.node, hash, size, type, source, mtime, "
647 ba402621 Sofia Papagiannaki
                      "muser, uuid, checksum, cluster"),
648 29148653 Sofia Papagiannaki
                     subq,
649 ba402621 Sofia Papagiannaki
                     "order by path" if order_by_path else "")
650 2715ade4 Sofia Papagiannaki
651 585b75e7 Sofia Papagiannaki
        args += [cluster]
652 4d15c94e Sofia Papagiannaki
        self.execute(q, args)
653 4d15c94e Sofia Papagiannaki
        return self.fetchall()
654 2715ade4 Sofia Papagiannaki
655 d2fc71c9 Sofia Papagiannaki
    def version_get_properties(self, serial, keys=(), propnames=_propnames,
656 d2fc71c9 Sofia Papagiannaki
                               node=None):
657 1f2d1b42 Antony Chazapis
        """Return a sequence of values for the properties of
658 1f2d1b42 Antony Chazapis
           the version specified by serial and the keys, in the order given.
659 1f2d1b42 Antony Chazapis
           If keys is empty, return all properties in the order
660 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
661 29148653 Sofia Papagiannaki
            checksum, cluster).
662 1f2d1b42 Antony Chazapis
        """
663 2715ade4 Sofia Papagiannaki
664 29148653 Sofia Papagiannaki
        q = ("select serial, node, hash, size, type, source, mtime, muser, "
665 29148653 Sofia Papagiannaki
             "uuid, checksum, cluster "
666 62f915a1 Antony Chazapis
             "from versions "
667 d2fc71c9 Sofia Papagiannaki
             "where serial = ? ")
668 d2fc71c9 Sofia Papagiannaki
        args = [serial]
669 d2fc71c9 Sofia Papagiannaki
        if node is not None:
670 d2fc71c9 Sofia Papagiannaki
            q += ("and node = ?")
671 d2fc71c9 Sofia Papagiannaki
            args += [node]
672 d2fc71c9 Sofia Papagiannaki
        self.execute(q, args)
673 1f2d1b42 Antony Chazapis
        r = self.fetchone()
674 1f2d1b42 Antony Chazapis
        if r is None:
675 1f2d1b42 Antony Chazapis
            return r
676 2715ade4 Sofia Papagiannaki
677 1f2d1b42 Antony Chazapis
        if not keys:
678 1f2d1b42 Antony Chazapis
            return r
679 1f2d1b42 Antony Chazapis
        return [r[propnames[k]] for k in keys if k in propnames]
680 2715ade4 Sofia Papagiannaki
681 33b4e4a6 Antony Chazapis
    def version_put_property(self, serial, key, value):
682 33b4e4a6 Antony Chazapis
        """Set value for the property of version specified by key."""
683 2715ade4 Sofia Papagiannaki
684 33b4e4a6 Antony Chazapis
        if key not in _propnames:
685 33b4e4a6 Antony Chazapis
            return
686 33b4e4a6 Antony Chazapis
        q = "update versions set %s = ? where serial = ?" % key
687 33b4e4a6 Antony Chazapis
        self.execute(q, (value, serial))
688 2715ade4 Sofia Papagiannaki
689 0534576c Sofia Papagiannaki
    def version_recluster(self, serial, cluster,
690 0534576c Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
691 44ad5860 Antony Chazapis
        """Move the version into another cluster."""
692 2715ade4 Sofia Papagiannaki
693 62f915a1 Antony Chazapis
        props = self.version_get_properties(serial)
694 62f915a1 Antony Chazapis
        if not props:
695 62f915a1 Antony Chazapis
            return
696 44ad5860 Antony Chazapis
        node = props[NODE]
697 44ad5860 Antony Chazapis
        size = props[SIZE]
698 44ad5860 Antony Chazapis
        oldcluster = props[CLUSTER]
699 44ad5860 Antony Chazapis
        if cluster == oldcluster:
700 44ad5860 Antony Chazapis
            return
701 2715ade4 Sofia Papagiannaki
702 62f915a1 Antony Chazapis
        mtime = time()
703 0534576c Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster,
704 0534576c Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
705 0534576c Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster,
706 0534576c Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
707 2715ade4 Sofia Papagiannaki
708 62f915a1 Antony Chazapis
        q = "update versions set cluster = ? where serial = ?"
709 c915d3bf Antony Chazapis
        self.execute(q, (cluster, serial))
710 2715ade4 Sofia Papagiannaki
711 0534576c Sofia Papagiannaki
    def version_remove(self, serial, update_statistics_ancestors_depth=None):
712 62f915a1 Antony Chazapis
        """Remove the serial specified."""
713 2715ade4 Sofia Papagiannaki
714 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
715 62f915a1 Antony Chazapis
        if not props:
716 62f915a1 Antony Chazapis
            return
717 62f915a1 Antony Chazapis
        node = props[NODE]
718 5161c672 Antony Chazapis
        hash = props[HASH]
719 62f915a1 Antony Chazapis
        size = props[SIZE]
720 62f915a1 Antony Chazapis
        cluster = props[CLUSTER]
721 2715ade4 Sofia Papagiannaki
722 62f915a1 Antony Chazapis
        mtime = time()
723 0534576c Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster,
724 0534576c Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
725 2715ade4 Sofia Papagiannaki
726 62f915a1 Antony Chazapis
        q = "delete from versions where serial = ?"
727 62f915a1 Antony Chazapis
        self.execute(q, (serial,))
728 2715ade4 Sofia Papagiannaki
729 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
730 585b75e7 Sofia Papagiannaki
        if props:
731 2715ade4 Sofia Papagiannaki
            self.nodes_set_latest_version(node, props[0])
732 813e42e5 Antony Chazapis
        return hash, size
733 2715ade4 Sofia Papagiannaki
734 4819d34f Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
735 29148653 Sofia Papagiannaki
        """Return a list of (key, value) pairs of the specific version.
736 29148653 Sofia Papagiannaki

737 1f2d1b42 Antony Chazapis
           If keys is empty, return all attributes.
738 1f2d1b42 Antony Chazapis
           Othwerise, return only those specified.
739 1f2d1b42 Antony Chazapis
        """
740 2715ade4 Sofia Papagiannaki
741 1f2d1b42 Antony Chazapis
        execute = self.execute
742 1f2d1b42 Antony Chazapis
        if keys:
743 1f2d1b42 Antony Chazapis
            marks = ','.join('?' for k in keys)
744 1f2d1b42 Antony Chazapis
            q = ("select key, value from attributes "
745 4819d34f Antony Chazapis
                 "where key in (%s) and serial = ? and domain = ?" % (marks,))
746 4819d34f Antony Chazapis
            execute(q, keys + (serial, domain))
747 1f2d1b42 Antony Chazapis
        else:
748 29148653 Sofia Papagiannaki
            q = ("select key, value from attributes where "
749 29148653 Sofia Papagiannaki
                 "serial = ? and domain = ?")
750 059857e2 Antony Chazapis
            execute(q, (serial, domain))
751 1f2d1b42 Antony Chazapis
        return self.fetchall()
752 2715ade4 Sofia Papagiannaki
753 0534576c Sofia Papagiannaki
    def attribute_set(self, serial, domain, node, items, is_latest=True):
754 44ad5860 Antony Chazapis
        """Set the attributes of the version specified by serial.
755 1f2d1b42 Antony Chazapis
           Receive attributes as an iterable of (key, value) pairs.
756 1f2d1b42 Antony Chazapis
        """
757 2715ade4 Sofia Papagiannaki
758 0534576c Sofia Papagiannaki
        q = ("insert or replace into attributes "
759 0534576c Sofia Papagiannaki
             "(serial, domain, node, is_latest, key, value) "
760 0534576c Sofia Papagiannaki
             "values (?, ?, ?, ?, ?, ?)")
761 0534576c Sofia Papagiannaki
        self.executemany(q, ((serial, domain, node, is_latest, k, v) for
762 29148653 Sofia Papagiannaki
                         k, v in items))
763 2715ade4 Sofia Papagiannaki
764 4819d34f Antony Chazapis
    def attribute_del(self, serial, domain, keys=()):
765 44ad5860 Antony Chazapis
        """Delete attributes of the version specified by serial.
766 1f2d1b42 Antony Chazapis
           If keys is empty, delete all attributes.
767 1f2d1b42 Antony Chazapis
           Otherwise delete those specified.
768 1f2d1b42 Antony Chazapis
        """
769 2715ade4 Sofia Papagiannaki
770 1f2d1b42 Antony Chazapis
        if keys:
771 29148653 Sofia Papagiannaki
            q = ("delete from attributes "
772 29148653 Sofia Papagiannaki
                 "where serial = ? and domain = ? and key = ?")
773 4819d34f Antony Chazapis
            self.executemany(q, ((serial, domain, key) for key in keys))
774 1f2d1b42 Antony Chazapis
        else:
775 4819d34f Antony Chazapis
            q = "delete from attributes where serial = ? and domain = ?"
776 4819d34f Antony Chazapis
            self.execute(q, (serial, domain))
777 2715ade4 Sofia Papagiannaki
778 44ad5860 Antony Chazapis
    def attribute_copy(self, source, dest):
779 1f2d1b42 Antony Chazapis
        q = ("insert or replace into attributes "
780 d3c34119 Sofia Papagiannaki
             "(serial, domain, node, is_latest, key, value) "
781 0534576c Sofia Papagiannaki
             "select ?, domain, node, is_latest, key, value from attributes "
782 1f2d1b42 Antony Chazapis
             "where serial = ?")
783 1f2d1b42 Antony Chazapis
        self.execute(q, (dest, source))
784 2715ade4 Sofia Papagiannaki
785 0534576c Sofia Papagiannaki
    def attribute_unset_is_latest(self, node, exclude):
786 0534576c Sofia Papagiannaki
        q = ("update attributes set is_latest = 0 "
787 0534576c Sofia Papagiannaki
             "where node = ? and serial != ?")
788 0534576c Sofia Papagiannaki
        self.execute(q, (node, exclude))
789 0534576c Sofia Papagiannaki
790 4819d34f Antony Chazapis
    def _construct_filters(self, domain, filterq):
791 4819d34f Antony Chazapis
        if not domain or not filterq:
792 60b8a083 Antony Chazapis
            return None, None
793 2715ade4 Sofia Papagiannaki
794 95d47e1a Antony Chazapis
        subqlist = []
795 95d47e1a Antony Chazapis
        append = subqlist.append
796 4819d34f Antony Chazapis
        included, excluded, opers = parse_filters(filterq)
797 95d47e1a Antony Chazapis
        args = []
798 2715ade4 Sofia Papagiannaki
799 95d47e1a Antony Chazapis
        if included:
800 29148653 Sofia Papagiannaki
            subq = ("exists (select 1 from attributes where serial = v.serial "
801 29148653 Sofia Papagiannaki
                    "and domain = ? and ")
802 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in included)) + ")"
803 c7c0790f chazapis
            subq += ")"
804 c7c0790f chazapis
            args += [domain]
805 95d47e1a Antony Chazapis
            args += included
806 95d47e1a Antony Chazapis
            append(subq)
807 2715ade4 Sofia Papagiannaki
808 95d47e1a Antony Chazapis
        if excluded:
809 29148653 Sofia Papagiannaki
            subq = ("not exists (select 1 from attributes where "
810 29148653 Sofia Papagiannaki
                    "serial = v.serial and domain = ? and ")
811 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in excluded)) + ")"
812 c7c0790f chazapis
            subq += ")"
813 c7c0790f chazapis
            args += [domain]
814 95d47e1a Antony Chazapis
            args += excluded
815 95d47e1a Antony Chazapis
            append(subq)
816 2715ade4 Sofia Papagiannaki
817 95d47e1a Antony Chazapis
        if opers:
818 95d47e1a Antony Chazapis
            for k, o, v in opers:
819 29148653 Sofia Papagiannaki
                subq = ("exists (select 1 from attributes where "
820 29148653 Sofia Papagiannaki
                        "serial = v.serial and domain = ? and ")
821 6b2f11b4 Antony Chazapis
                subq += "key = ? and value %s ?" % (o,)
822 6b2f11b4 Antony Chazapis
                subq += ")"
823 6b2f11b4 Antony Chazapis
                args += [domain, k, v]
824 6b2f11b4 Antony Chazapis
                append(subq)
825 2715ade4 Sofia Papagiannaki
826 95d47e1a Antony Chazapis
        if not subqlist:
827 95d47e1a Antony Chazapis
            return None, None
828 2715ade4 Sofia Papagiannaki
829 c7c0790f chazapis
        subq = ' and ' + ' and '.join(subqlist)
830 2715ade4 Sofia Papagiannaki
831 60b8a083 Antony Chazapis
        return subq, args
832 2715ade4 Sofia Papagiannaki
833 60b8a083 Antony Chazapis
    def _construct_paths(self, pathq):
834 60b8a083 Antony Chazapis
        if not pathq:
835 60b8a083 Antony Chazapis
            return None, None
836 2715ade4 Sofia Papagiannaki
837 d57eaad4 Antony Chazapis
        subqlist = []
838 d57eaad4 Antony Chazapis
        args = []
839 d57eaad4 Antony Chazapis
        for path, match in pathq:
840 d57eaad4 Antony Chazapis
            if match == MATCH_PREFIX:
841 d57eaad4 Antony Chazapis
                subqlist.append("n.path like ? escape '\\'")
842 d57eaad4 Antony Chazapis
                args.append(self.escape_like(path) + '%')
843 d57eaad4 Antony Chazapis
            elif match == MATCH_EXACT:
844 d57eaad4 Antony Chazapis
                subqlist.append("n.path = ?")
845 d57eaad4 Antony Chazapis
                args.append(path)
846 2715ade4 Sofia Papagiannaki
847 d57eaad4 Antony Chazapis
        subq = ' and (' + ' or '.join(subqlist) + ')'
848 d57eaad4 Antony Chazapis
        args = tuple(args)
849 2715ade4 Sofia Papagiannaki
850 60b8a083 Antony Chazapis
        return subq, args
851 2715ade4 Sofia Papagiannaki
852 7ff57991 Antony Chazapis
    def _construct_size(self, sizeq):
853 7ff57991 Antony Chazapis
        if not sizeq or len(sizeq) != 2:
854 7ff57991 Antony Chazapis
            return None, None
855 2715ade4 Sofia Papagiannaki
856 7ff57991 Antony Chazapis
        subq = ''
857 7ff57991 Antony Chazapis
        args = []
858 7ff57991 Antony Chazapis
        if sizeq[0]:
859 7ff57991 Antony Chazapis
            subq += " and v.size >= ?"
860 7ff57991 Antony Chazapis
            args += [sizeq[0]]
861 7ff57991 Antony Chazapis
        if sizeq[1]:
862 7ff57991 Antony Chazapis
            subq += " and v.size < ?"
863 7ff57991 Antony Chazapis
            args += [sizeq[1]]
864 2715ade4 Sofia Papagiannaki
865 7ff57991 Antony Chazapis
        return subq, args
866 2715ade4 Sofia Papagiannaki
867 585b75e7 Sofia Papagiannaki
    def _construct_versions_nodes_latest_version_subquery(self, before=inf):
868 585b75e7 Sofia Papagiannaki
        if before == inf:
869 585b75e7 Sofia Papagiannaki
            q = ("n.latest_version ")
870 585b75e7 Sofia Papagiannaki
            args = []
871 585b75e7 Sofia Papagiannaki
        else:
872 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
873 2715ade4 Sofia Papagiannaki
                 "from versions "
874 2715ade4 Sofia Papagiannaki
                 "where node = v.node and mtime < ?) ")
875 585b75e7 Sofia Papagiannaki
            args = [before]
876 585b75e7 Sofia Papagiannaki
        return q, args
877 2715ade4 Sofia Papagiannaki
878 585b75e7 Sofia Papagiannaki
    def _construct_latest_version_subquery(self, node=None, before=inf):
879 585b75e7 Sofia Papagiannaki
        where_cond = "node = v.node"
880 585b75e7 Sofia Papagiannaki
        args = []
881 585b75e7 Sofia Papagiannaki
        if node:
882 585b75e7 Sofia Papagiannaki
            where_cond = "node = ? "
883 585b75e7 Sofia Papagiannaki
            args = [node]
884 2715ade4 Sofia Papagiannaki
885 585b75e7 Sofia Papagiannaki
        if before == inf:
886 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
887 2715ade4 Sofia Papagiannaki
                 "from nodes "
888 2715ade4 Sofia Papagiannaki
                 "where %s) ")
889 585b75e7 Sofia Papagiannaki
        else:
890 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
891 2715ade4 Sofia Papagiannaki
                 "from versions "
892 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ?) ")
893 585b75e7 Sofia Papagiannaki
            args += [before]
894 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
895 2715ade4 Sofia Papagiannaki
896 585b75e7 Sofia Papagiannaki
    def _construct_latest_versions_subquery(self, nodes=(), before=inf):
897 585b75e7 Sofia Papagiannaki
        where_cond = ""
898 585b75e7 Sofia Papagiannaki
        args = []
899 585b75e7 Sofia Papagiannaki
        if nodes:
900 585b75e7 Sofia Papagiannaki
            where_cond = "node in (%s) " % ','.join('?' for node in nodes)
901 585b75e7 Sofia Papagiannaki
            args = nodes
902 2715ade4 Sofia Papagiannaki
903 585b75e7 Sofia Papagiannaki
        if before == inf:
904 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
905 2715ade4 Sofia Papagiannaki
                 "from nodes "
906 2715ade4 Sofia Papagiannaki
                 "where %s ) ")
907 585b75e7 Sofia Papagiannaki
        else:
908 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
909 2715ade4 Sofia Papagiannaki
                 "from versions "
910 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ? group by node) ")
911 585b75e7 Sofia Papagiannaki
            args += [before]
912 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
913 2715ade4 Sofia Papagiannaki
914 29148653 Sofia Papagiannaki
    def latest_attribute_keys(self, parent, domain, before=inf,
915 29148653 Sofia Papagiannaki
                              except_cluster=0, pathq=None):
916 676edf89 Antony Chazapis
        """Return a list with all keys pairs defined
917 676edf89 Antony Chazapis
           for all latest versions under parent that
918 676edf89 Antony Chazapis
           do not belong to the cluster.
919 676edf89 Antony Chazapis
        """
920 2715ade4 Sofia Papagiannaki
921 78348987 Sofia Papagiannaki
        pathq = pathq or []
922 78348987 Sofia Papagiannaki
923 676edf89 Antony Chazapis
        # TODO: Use another table to store before=inf results.
924 60b8a083 Antony Chazapis
        q = ("select distinct a.key "
925 676edf89 Antony Chazapis
             "from attributes a, versions v, nodes n "
926 585b75e7 Sofia Papagiannaki
             "where v.serial = %s "
927 676edf89 Antony Chazapis
             "and v.cluster != ? "
928 676edf89 Antony Chazapis
             "and v.node in (select node "
929 2715ade4 Sofia Papagiannaki
             "from nodes "
930 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
931 676edf89 Antony Chazapis
             "and a.serial = v.serial "
932 4819d34f Antony Chazapis
             "and a.domain = ? "
933 676edf89 Antony Chazapis
             "and n.node = v.node")
934 2715ade4 Sofia Papagiannaki
        subq, subargs = self._construct_latest_version_subquery(
935 2715ade4 Sofia Papagiannaki
            node=None, before=before)
936 585b75e7 Sofia Papagiannaki
        args = subargs + [except_cluster, parent, domain]
937 585b75e7 Sofia Papagiannaki
        q = q % subq
938 60b8a083 Antony Chazapis
        subq, subargs = self._construct_paths(pathq)
939 60b8a083 Antony Chazapis
        if subq is not None:
940 60b8a083 Antony Chazapis
            q += subq
941 60b8a083 Antony Chazapis
            args += subargs
942 676edf89 Antony Chazapis
        self.execute(q, args)
943 676edf89 Antony Chazapis
        return [r[0] for r in self.fetchall()]
944 2715ade4 Sofia Papagiannaki
945 60b8a083 Antony Chazapis
    def latest_version_list(self, parent, prefix='', delimiter=None,
946 60b8a083 Antony Chazapis
                            start='', limit=10000, before=inf,
947 371d907a Antony Chazapis
                            except_cluster=0, pathq=[], domain=None,
948 371d907a Antony Chazapis
                            filterq=[], sizeq=None, all_props=False):
949 60b8a083 Antony Chazapis
        """Return a (list of (path, serial) tuples, list of common prefixes)
950 60b8a083 Antony Chazapis
           for the current versions of the paths with the given parent,
951 60b8a083 Antony Chazapis
           matching the following criteria.
952 2715ade4 Sofia Papagiannaki

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

956 60b8a083 Antony Chazapis
                a. parent matches
957 2715ade4 Sofia Papagiannaki

958 60b8a083 Antony Chazapis
                b. path > start
959 2715ade4 Sofia Papagiannaki

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

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

964 60b8a083 Antony Chazapis
                e. version is not in cluster
965 2715ade4 Sofia Papagiannaki

966 60b8a083 Antony Chazapis
                f. the path does not have the delimiter occuring
967 60b8a083 Antony Chazapis
                   after the prefix, or ends with the delimiter
968 2715ade4 Sofia Papagiannaki

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

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

974 60b8a083 Antony Chazapis
                   key
975 60b8a083 Antony Chazapis
                       an attribute with this key must exist
976 2715ade4 Sofia Papagiannaki

977 60b8a083 Antony Chazapis
                   !key
978 60b8a083 Antony Chazapis
                       an attribute with this key must not exist
979 2715ade4 Sofia Papagiannaki

980 60b8a083 Antony Chazapis
                   key ?op value
981 60b8a083 Antony Chazapis
                       the attribute with this key satisfies the value
982 1713c946 Antony Chazapis
                       where ?op is one of =, != <=, >=, <, >.
983 2715ade4 Sofia Papagiannaki

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

986 60b8a083 Antony Chazapis
           The list of common prefixes includes the prefixes
987 60b8a083 Antony Chazapis
           matching up to the first delimiter after prefix,
988 60b8a083 Antony Chazapis
           and are reported only once, as "virtual directories".
989 60b8a083 Antony Chazapis
           The delimiter is included in the prefixes.
990 2715ade4 Sofia Papagiannaki

991 60b8a083 Antony Chazapis
           If arguments are None, then the corresponding matching rule
992 60b8a083 Antony Chazapis
           will always match.
993 2715ade4 Sofia Papagiannaki

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

996 29148653 Sofia Papagiannaki
           If all_props is True, return all properties after path,
997 29148653 Sofia Papagiannaki
           not just serial.
998 60b8a083 Antony Chazapis
        """
999 2715ade4 Sofia Papagiannaki
1000 60b8a083 Antony Chazapis
        execute = self.execute
1001 2715ade4 Sofia Papagiannaki
1002 60b8a083 Antony Chazapis
        if not start or start < prefix:
1003 60b8a083 Antony Chazapis
            start = strprevling(prefix)
1004 60b8a083 Antony Chazapis
        nextling = strnextling(prefix)
1005 2715ade4 Sofia Papagiannaki
1006 371d907a Antony Chazapis
        q = ("select distinct n.path, %s "
1007 c7c0790f chazapis
             "from versions v, nodes n "
1008 585b75e7 Sofia Papagiannaki
             "where v.serial = %s "
1009 60b8a083 Antony Chazapis
             "and v.cluster != ? "
1010 60b8a083 Antony Chazapis
             "and v.node in (select node "
1011 2715ade4 Sofia Papagiannaki
             "from nodes "
1012 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
1013 60b8a083 Antony Chazapis
             "and n.node = v.node "
1014 60b8a083 Antony Chazapis
             "and n.path > ? and n.path < ?")
1015 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_versions_nodes_latest_version_subquery(
1016 2715ade4 Sofia Papagiannaki
            before)
1017 371d907a Antony Chazapis
        if not all_props:
1018 585b75e7 Sofia Papagiannaki
            q = q % ("v.serial", subq)
1019 371d907a Antony Chazapis
        else:
1020 29148653 Sofia Papagiannaki
            q = q % (("v.serial, v.node, v.hash, v.size, v.type, v.source, "
1021 29148653 Sofia Papagiannaki
                      "v.mtime, v.muser, v.uuid, v.checksum, v.cluster"),
1022 29148653 Sofia Papagiannaki
                     subq)
1023 585b75e7 Sofia Papagiannaki
        args += [except_cluster, parent, start, nextling]
1024 585b75e7 Sofia Papagiannaki
        start_index = len(args) - 2
1025 2715ade4 Sofia Papagiannaki
1026 60b8a083 Antony Chazapis
        subq, subargs = self._construct_paths(pathq)
1027 60b8a083 Antony Chazapis
        if subq is not None:
1028 60b8a083 Antony Chazapis
            q += subq
1029 60b8a083 Antony Chazapis
            args += subargs
1030 7ff57991 Antony Chazapis
        subq, subargs = self._construct_size(sizeq)
1031 7ff57991 Antony Chazapis
        if subq is not None:
1032 7ff57991 Antony Chazapis
            q += subq
1033 7ff57991 Antony Chazapis
            args += subargs
1034 4819d34f Antony Chazapis
        subq, subargs = self._construct_filters(domain, filterq)
1035 60b8a083 Antony Chazapis
        if subq is not None:
1036 60b8a083 Antony Chazapis
            q += subq
1037 60b8a083 Antony Chazapis
            args += subargs
1038 60b8a083 Antony Chazapis
        else:
1039 60b8a083 Antony Chazapis
            q = q.replace("attributes a, ", "")
1040 60b8a083 Antony Chazapis
            q = q.replace("and a.serial = v.serial ", "")
1041 60b8a083 Antony Chazapis
        q += " order by n.path"
1042 2715ade4 Sofia Papagiannaki
1043 60b8a083 Antony Chazapis
        if not delimiter:
1044 60b8a083 Antony Chazapis
            q += " limit ?"
1045 60b8a083 Antony Chazapis
            args.append(limit)
1046 60b8a083 Antony Chazapis
            execute(q, args)
1047 60b8a083 Antony Chazapis
            return self.fetchall(), ()
1048 2715ade4 Sofia Papagiannaki
1049 60b8a083 Antony Chazapis
        pfz = len(prefix)
1050 60b8a083 Antony Chazapis
        dz = len(delimiter)
1051 60b8a083 Antony Chazapis
        count = 0
1052 60b8a083 Antony Chazapis
        fetchone = self.fetchone
1053 60b8a083 Antony Chazapis
        prefixes = []
1054 60b8a083 Antony Chazapis
        pappend = prefixes.append
1055 60b8a083 Antony Chazapis
        matches = []
1056 60b8a083 Antony Chazapis
        mappend = matches.append
1057 2715ade4 Sofia Papagiannaki
1058 60b8a083 Antony Chazapis
        execute(q, args)
1059 60b8a083 Antony Chazapis
        while True:
1060 60b8a083 Antony Chazapis
            props = fetchone()
1061 60b8a083 Antony Chazapis
            if props is None:
1062 60b8a083 Antony Chazapis
                break
1063 371d907a Antony Chazapis
            path = props[0]
1064 60b8a083 Antony Chazapis
            idx = path.find(delimiter, pfz)
1065 2715ade4 Sofia Papagiannaki
1066 60b8a083 Antony Chazapis
            if idx < 0:
1067 60b8a083 Antony Chazapis
                mappend(props)
1068 60b8a083 Antony Chazapis
                count += 1
1069 60b8a083 Antony Chazapis
                if count >= limit:
1070 60b8a083 Antony Chazapis
                    break
1071 60b8a083 Antony Chazapis
                continue
1072 2715ade4 Sofia Papagiannaki
1073 60b8a083 Antony Chazapis
            if idx + dz == len(path):
1074 60b8a083 Antony Chazapis
                mappend(props)
1075 60b8a083 Antony Chazapis
                count += 1
1076 2715ade4 Sofia Papagiannaki
                continue  # Get one more, in case there is a path.
1077 4cc00e08 Antony Chazapis
            pf = path[:idx + dz]
1078 4cc00e08 Antony Chazapis
            pappend(pf)
1079 2715ade4 Sofia Papagiannaki
            if count >= limit:
1080 60b8a083 Antony Chazapis
                break
1081 2715ade4 Sofia Papagiannaki
1082 2715ade4 Sofia Papagiannaki
            args[start_index] = strnextling(pf)  # New start.
1083 60b8a083 Antony Chazapis
            execute(q, args)
1084 2715ade4 Sofia Papagiannaki
1085 60b8a083 Antony Chazapis
        return matches, prefixes
1086 2715ade4 Sofia Papagiannaki
1087 2bbf1544 Georgios D. Tsoukalas
    def latest_uuid(self, uuid, cluster):
1088 2bbf1544 Georgios D. Tsoukalas
        """Return the latest version of the given uuid and cluster.
1089 2bbf1544 Georgios D. Tsoukalas

1090 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1091 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1092 2bbf1544 Georgios D. Tsoukalas

1093 2bbf1544 Georgios D. Tsoukalas
        """
1094 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
1095 2bbf1544 Georgios D. Tsoukalas
            cluster_where = "and cluster = ?"
1096 2bbf1544 Georgios D. Tsoukalas
            args = (uuid, int(cluster))
1097 2bbf1544 Georgios D. Tsoukalas
        else:
1098 2bbf1544 Georgios D. Tsoukalas
            cluster_where = ""
1099 2bbf1544 Georgios D. Tsoukalas
            args = (uuid,)
1100 2715ade4 Sofia Papagiannaki
1101 37bee317 Antony Chazapis
        q = ("select n.path, v.serial "
1102 37bee317 Antony Chazapis
             "from versions v, nodes n "
1103 37bee317 Antony Chazapis
             "where v.serial = (select max(serial) "
1104 2715ade4 Sofia Papagiannaki
             "from versions "
1105 2bbf1544 Georgios D. Tsoukalas
             "where uuid = ? %s) "
1106 2bbf1544 Georgios D. Tsoukalas
             "and n.node = v.node") % cluster_where
1107 2bbf1544 Georgios D. Tsoukalas
        self.execute(q, args)
1108 37bee317 Antony Chazapis
        return self.fetchone()
1109 5576e6dd Sofia Papagiannaki
1110 7736e11a Sofia Papagiannaki
    def domain_object_list(self, domain, paths, cluster=None):
1111 5576e6dd Sofia Papagiannaki
        """Return a list of (path, property list, attribute dictionary)
1112 5576e6dd Sofia Papagiannaki
           for the objects in the specific domain and cluster.
1113 5576e6dd Sofia Papagiannaki
        """
1114 5576e6dd Sofia Papagiannaki
1115 5576e6dd Sofia Papagiannaki
        q = ("select n.path, v.serial, v.node, v.hash, "
1116 5576e6dd Sofia Papagiannaki
             "v.size, v.type, v.source, v.mtime, v.muser, "
1117 5576e6dd Sofia Papagiannaki
             "v.uuid, v.checksum, v.cluster, a.key, a.value "
1118 5576e6dd Sofia Papagiannaki
             "from nodes n, versions v, attributes a "
1119 0534576c Sofia Papagiannaki
             "where v.serial = a.serial and "
1120 7736e11a Sofia Papagiannaki
             "a.domain = ? and "
1121 0534576c Sofia Papagiannaki
             "a.node = n.node and "
1122 0534576c Sofia Papagiannaki
             "a.is_latest = 1 and "
1123 a5b0519c Georgios D. Tsoukalas
             "n.path in (%s)") % ','.join('?' for _ in paths)
1124 5576e6dd Sofia Papagiannaki
        args = [domain]
1125 7736e11a Sofia Papagiannaki
        map(args.append, paths)
1126 29148653 Sofia Papagiannaki
        if cluster is not None:
1127 5576e6dd Sofia Papagiannaki
            q += "and v.cluster = ?"
1128 5576e6dd Sofia Papagiannaki
            args += [cluster]
1129 5576e6dd Sofia Papagiannaki
1130 5576e6dd Sofia Papagiannaki
        self.execute(q, args)
1131 5576e6dd Sofia Papagiannaki
        rows = self.fetchall()
1132 5576e6dd Sofia Papagiannaki
1133 5576e6dd Sofia Papagiannaki
        group_by = itemgetter(slice(12))
1134 29148653 Sofia Papagiannaki
        rows.sort(key=group_by)
1135 5576e6dd Sofia Papagiannaki
        groups = groupby(rows, group_by)
1136 29148653 Sofia Papagiannaki
        return [(k[0], k[1:], dict([i[12:] for i in data])) for
1137 29148653 Sofia Papagiannaki
                (k, data) in groups]
1138 d3c34119 Sofia Papagiannaki
1139 d3c34119 Sofia Papagiannaki
    def get_props(self, paths):
1140 d3c34119 Sofia Papagiannaki
        q = ("select distinct n.path, v.type "
1141 d3c34119 Sofia Papagiannaki
             "from nodes n inner join versions v "
1142 d3c34119 Sofia Papagiannaki
             "on v.serial = n.latest_version "
1143 d3c34119 Sofia Papagiannaki
             "where n.path in (%s)") % ','.join('?' for _ in paths)
1144 d3c34119 Sofia Papagiannaki
        self.execute(q, paths)
1145 d3c34119 Sofia Papagiannaki
        return self.fetchall()