Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (34.1 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 1f2d1b42 Antony Chazapis
36 1f2d1b42 Antony Chazapis
from dbworker import DBWorker
37 1f2d1b42 Antony Chazapis
38 6e147ecc Antony Chazapis
from pithos.backends.filter import parse_filters
39 1713c946 Antony Chazapis
40 1f2d1b42 Antony Chazapis
41 2715ade4 Sofia Papagiannaki
ROOTNODE = 0
42 1f2d1b42 Antony Chazapis
43 2715ade4 Sofia Papagiannaki
(SERIAL, NODE, HASH, SIZE, TYPE, SOURCE, MTIME, MUSER, UUID, CHECKSUM,
44 2715ade4 Sofia Papagiannaki
 CLUSTER) = range(11)
45 1f2d1b42 Antony Chazapis
46 2715ade4 Sofia Papagiannaki
(MATCH_PREFIX, MATCH_EXACT) = range(2)
47 1f2d1b42 Antony Chazapis
48 1f2d1b42 Antony Chazapis
inf = float('inf')
49 1f2d1b42 Antony Chazapis
50 1f2d1b42 Antony Chazapis
51 1f2d1b42 Antony Chazapis
def strnextling(prefix):
52 60b8a083 Antony Chazapis
    """Return the first unicode string
53 1f2d1b42 Antony Chazapis
       greater than but not starting with given prefix.
54 1f2d1b42 Antony Chazapis
       strnextling('hello') -> 'hellp'
55 1f2d1b42 Antony Chazapis
    """
56 1f2d1b42 Antony Chazapis
    if not prefix:
57 1f2d1b42 Antony Chazapis
        ## all strings start with the null string,
58 1f2d1b42 Antony Chazapis
        ## therefore we have to approximate strnextling('')
59 1f2d1b42 Antony Chazapis
        ## with the last unicode character supported by python
60 1f2d1b42 Antony Chazapis
        ## 0x10ffff for wide (32-bit unicode) python builds
61 1f2d1b42 Antony Chazapis
        ## 0x00ffff for narrow (16-bit unicode) python builds
62 1f2d1b42 Antony Chazapis
        ## We will not autodetect. 0xffff is safe enough.
63 1f2d1b42 Antony Chazapis
        return unichr(0xffff)
64 1f2d1b42 Antony Chazapis
    s = prefix[:-1]
65 1f2d1b42 Antony Chazapis
    c = ord(prefix[-1])
66 1f2d1b42 Antony Chazapis
    if c >= 0xffff:
67 1f2d1b42 Antony Chazapis
        raise RuntimeError
68 2715ade4 Sofia Papagiannaki
    s += unichr(c + 1)
69 1f2d1b42 Antony Chazapis
    return s
70 1f2d1b42 Antony Chazapis
71 2715ade4 Sofia Papagiannaki
72 1f2d1b42 Antony Chazapis
def strprevling(prefix):
73 60b8a083 Antony Chazapis
    """Return an approximation of the last unicode string
74 1f2d1b42 Antony Chazapis
       less than but not starting with given prefix.
75 1f2d1b42 Antony Chazapis
       strprevling(u'hello') -> u'helln\\xffff'
76 1f2d1b42 Antony Chazapis
    """
77 1f2d1b42 Antony Chazapis
    if not prefix:
78 1f2d1b42 Antony Chazapis
        ## There is no prevling for the null string
79 1f2d1b42 Antony Chazapis
        return prefix
80 1f2d1b42 Antony Chazapis
    s = prefix[:-1]
81 1f2d1b42 Antony Chazapis
    c = ord(prefix[-1])
82 1f2d1b42 Antony Chazapis
    if c > 0:
83 2715ade4 Sofia Papagiannaki
        s += unichr(c - 1) + unichr(0xffff)
84 1f2d1b42 Antony Chazapis
    return s
85 1f2d1b42 Antony Chazapis
86 1f2d1b42 Antony Chazapis
87 1f2d1b42 Antony Chazapis
_propnames = {
88 2715ade4 Sofia Papagiannaki
    'serial': 0,
89 2715ade4 Sofia Papagiannaki
    'node': 1,
90 2715ade4 Sofia Papagiannaki
    'hash': 2,
91 2715ade4 Sofia Papagiannaki
    'size': 3,
92 2715ade4 Sofia Papagiannaki
    'type': 4,
93 2715ade4 Sofia Papagiannaki
    'source': 5,
94 2715ade4 Sofia Papagiannaki
    'mtime': 6,
95 2715ade4 Sofia Papagiannaki
    'muser': 7,
96 2715ade4 Sofia Papagiannaki
    'uuid': 8,
97 2715ade4 Sofia Papagiannaki
    'checksum': 9,
98 2715ade4 Sofia Papagiannaki
    'cluster': 10
99 1f2d1b42 Antony Chazapis
}
100 1f2d1b42 Antony Chazapis
101 1f2d1b42 Antony Chazapis
102 1f2d1b42 Antony Chazapis
class Node(DBWorker):
103 60b8a083 Antony Chazapis
    """Nodes store path organization and have multiple versions.
104 60b8a083 Antony Chazapis
       Versions store object history and have multiple attributes.
105 1f2d1b42 Antony Chazapis
       Attributes store metadata.
106 1f2d1b42 Antony Chazapis
    """
107 2715ade4 Sofia Papagiannaki
108 60b8a083 Antony Chazapis
    # TODO: Provide an interface for included and excluded clusters.
109 2715ade4 Sofia Papagiannaki
110 1f2d1b42 Antony Chazapis
    def __init__(self, **params):
111 62f915a1 Antony Chazapis
        DBWorker.__init__(self, **params)
112 1f2d1b42 Antony Chazapis
        execute = self.execute
113 2715ade4 Sofia Papagiannaki
114 1f2d1b42 Antony Chazapis
        execute(""" pragma foreign_keys = on """)
115 2715ade4 Sofia Papagiannaki
116 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists nodes
117 1f2d1b42 Antony Chazapis
                          ( node       integer primary key,
118 62f915a1 Antony Chazapis
                            parent     integer default 0,
119 44ad5860 Antony Chazapis
                            path       text    not null default '',
120 585b75e7 Sofia Papagiannaki
                            latest_version     integer,
121 1f2d1b42 Antony Chazapis
                            foreign key (parent)
122 1f2d1b42 Antony Chazapis
                            references nodes(node)
123 1f2d1b42 Antony Chazapis
                            on update cascade
124 5e7485da Antony Chazapis
                            on delete cascade ) """)
125 1f2d1b42 Antony Chazapis
        execute(""" create unique index if not exists idx_nodes_path
126 1f2d1b42 Antony Chazapis
                    on nodes(path) """)
127 585b75e7 Sofia Papagiannaki
        execute(""" create index if not exists idx_nodes_parent
128 585b75e7 Sofia Papagiannaki
                    on nodes(parent) """)
129 2715ade4 Sofia Papagiannaki
130 5e7485da Antony Chazapis
        execute(""" create table if not exists policy
131 5e7485da Antony Chazapis
                          ( node   integer,
132 5e7485da Antony Chazapis
                            key    text,
133 5e7485da Antony Chazapis
                            value  text,
134 5e7485da Antony Chazapis
                            primary key (node, key)
135 5e7485da Antony Chazapis
                            foreign key (node)
136 5e7485da Antony Chazapis
                            references nodes(node)
137 5e7485da Antony Chazapis
                            on update cascade
138 5e7485da Antony Chazapis
                            on delete cascade ) """)
139 2715ade4 Sofia Papagiannaki
140 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists statistics
141 62f915a1 Antony Chazapis
                          ( node       integer,
142 1f2d1b42 Antony Chazapis
                            population integer not null default 0,
143 1f2d1b42 Antony Chazapis
                            size       integer not null default 0,
144 1f2d1b42 Antony Chazapis
                            mtime      integer,
145 1f2d1b42 Antony Chazapis
                            cluster    integer not null default 0,
146 1f2d1b42 Antony Chazapis
                            primary key (node, cluster)
147 1f2d1b42 Antony Chazapis
                            foreign key (node)
148 1f2d1b42 Antony Chazapis
                            references nodes(node)
149 1f2d1b42 Antony Chazapis
                            on update cascade
150 5e7485da Antony Chazapis
                            on delete cascade ) """)
151 2715ade4 Sofia Papagiannaki
152 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists versions
153 1f2d1b42 Antony Chazapis
                          ( serial     integer primary key,
154 62f915a1 Antony Chazapis
                            node       integer,
155 1c2fc0ff Antony Chazapis
                            hash       text,
156 1f2d1b42 Antony Chazapis
                            size       integer not null default 0,
157 66ce2ca5 Antony Chazapis
                            type       text    not null default '',
158 1f2d1b42 Antony Chazapis
                            source     integer,
159 1f2d1b42 Antony Chazapis
                            mtime      integer,
160 c1cdc455 Antony Chazapis
                            muser      text    not null default '',
161 25ae8b75 Antony Chazapis
                            uuid       text    not null default '',
162 33b4e4a6 Antony Chazapis
                            checksum   text    not null default '',
163 1f2d1b42 Antony Chazapis
                            cluster    integer not null default 0,
164 1f2d1b42 Antony Chazapis
                            foreign key (node)
165 1f2d1b42 Antony Chazapis
                            references nodes(node)
166 1f2d1b42 Antony Chazapis
                            on update cascade
167 1f2d1b42 Antony Chazapis
                            on delete cascade ) """)
168 9e35988c Antony Chazapis
        execute(""" create index if not exists idx_versions_node_mtime
169 9e35988c Antony Chazapis
                    on versions(node, mtime) """)
170 25ae8b75 Antony Chazapis
        execute(""" create index if not exists idx_versions_node_uuid
171 25ae8b75 Antony Chazapis
                    on versions(uuid) """)
172 2715ade4 Sofia Papagiannaki
173 1f2d1b42 Antony Chazapis
        execute(""" create table if not exists attributes
174 1f2d1b42 Antony Chazapis
                          ( serial integer,
175 4819d34f Antony Chazapis
                            domain text,
176 1f2d1b42 Antony Chazapis
                            key    text,
177 1f2d1b42 Antony Chazapis
                            value  text,
178 4819d34f Antony Chazapis
                            primary key (serial, domain, key)
179 1f2d1b42 Antony Chazapis
                            foreign key (serial)
180 1f2d1b42 Antony Chazapis
                            references versions(serial)
181 1f2d1b42 Antony Chazapis
                            on update cascade
182 1f2d1b42 Antony Chazapis
                            on delete cascade ) """)
183 2715ade4 Sofia Papagiannaki
184 1f2d1b42 Antony Chazapis
        q = "insert or ignore into nodes(node, parent) values (?, ?)"
185 1f2d1b42 Antony Chazapis
        execute(q, (ROOTNODE, ROOTNODE))
186 2715ade4 Sofia Papagiannaki
187 44ad5860 Antony Chazapis
    def node_create(self, parent, path):
188 44ad5860 Antony Chazapis
        """Create a new node from the given properties.
189 44ad5860 Antony Chazapis
           Return the node identifier of the new node.
190 44ad5860 Antony Chazapis
        """
191 2715ade4 Sofia Papagiannaki
192 44ad5860 Antony Chazapis
        q = ("insert into nodes (parent, path) "
193 44ad5860 Antony Chazapis
             "values (?, ?)")
194 44ad5860 Antony Chazapis
        props = (parent, path)
195 44ad5860 Antony Chazapis
        return self.execute(q, props).lastrowid
196 2715ade4 Sofia Papagiannaki
197 44ad5860 Antony Chazapis
    def node_lookup(self, path):
198 44ad5860 Antony Chazapis
        """Lookup the current node of the given path.
199 44ad5860 Antony Chazapis
           Return None if the path is not found.
200 44ad5860 Antony Chazapis
        """
201 2715ade4 Sofia Papagiannaki
202 c1cdc455 Antony Chazapis
        q = "select node from nodes where path = ?"
203 44ad5860 Antony Chazapis
        self.execute(q, (path,))
204 44ad5860 Antony Chazapis
        r = self.fetchone()
205 44ad5860 Antony Chazapis
        if r is not None:
206 44ad5860 Antony Chazapis
            return r[0]
207 44ad5860 Antony Chazapis
        return None
208 2715ade4 Sofia Papagiannaki
209 4d15c94e Sofia Papagiannaki
    def node_lookup_bulk(self, paths):
210 2715ade4 Sofia Papagiannaki
        """Lookup the current nodes for the given paths.
211 4d15c94e Sofia Papagiannaki
           Return () if the path is not found.
212 4d15c94e Sofia Papagiannaki
        """
213 2715ade4 Sofia Papagiannaki
214 4d15c94e Sofia Papagiannaki
        placeholders = ','.join('?' for path in paths)
215 cf4a7a7b Sofia Papagiannaki
        q = "select node from nodes where path in (%s)" % placeholders
216 4d15c94e Sofia Papagiannaki
        self.execute(q, paths)
217 cf4a7a7b Sofia Papagiannaki
        r = self.fetchall()
218 cf4a7a7b Sofia Papagiannaki
        if r is not None:
219 2715ade4 Sofia Papagiannaki
            return [row[0] for row in r]
220 cf4a7a7b Sofia Papagiannaki
        return None
221 2715ade4 Sofia Papagiannaki
222 62f915a1 Antony Chazapis
    def node_get_properties(self, node):
223 62f915a1 Antony Chazapis
        """Return the node's (parent, path).
224 62f915a1 Antony Chazapis
           Return None if the node is not found.
225 62f915a1 Antony Chazapis
        """
226 2715ade4 Sofia Papagiannaki
227 62f915a1 Antony Chazapis
        q = "select parent, path from nodes where node = ?"
228 c1cdc455 Antony Chazapis
        self.execute(q, (node,))
229 62f915a1 Antony Chazapis
        return self.fetchone()
230 2715ade4 Sofia Papagiannaki
231 676edf89 Antony Chazapis
    def node_get_versions(self, node, keys=(), propnames=_propnames):
232 676edf89 Antony Chazapis
        """Return the properties of all versions at node.
233 676edf89 Antony Chazapis
           If keys is empty, return all properties in the order
234 33b4e4a6 Antony Chazapis
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
235 676edf89 Antony Chazapis
        """
236 2715ade4 Sofia Papagiannaki
237 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
238 676edf89 Antony Chazapis
             "from versions "
239 676edf89 Antony Chazapis
             "where node = ?")
240 60b8a083 Antony Chazapis
        self.execute(q, (node,))
241 676edf89 Antony Chazapis
        r = self.fetchall()
242 676edf89 Antony Chazapis
        if r is None:
243 676edf89 Antony Chazapis
            return r
244 2715ade4 Sofia Papagiannaki
245 676edf89 Antony Chazapis
        if not keys:
246 676edf89 Antony Chazapis
            return r
247 676edf89 Antony Chazapis
        return [[p[propnames[k]] for k in keys if k in propnames] for p in r]
248 2715ade4 Sofia Papagiannaki
249 c915d3bf Antony Chazapis
    def node_count_children(self, node):
250 44ad5860 Antony Chazapis
        """Return node's child count."""
251 2715ade4 Sofia Papagiannaki
252 c915d3bf Antony Chazapis
        q = "select count(node) from nodes where parent = ? and node != 0"
253 44ad5860 Antony Chazapis
        self.execute(q, (node,))
254 62f915a1 Antony Chazapis
        r = self.fetchone()
255 44ad5860 Antony Chazapis
        if r is None:
256 44ad5860 Antony Chazapis
            return 0
257 62f915a1 Antony Chazapis
        return r[0]
258 2715ade4 Sofia Papagiannaki
259 c915d3bf Antony Chazapis
    def node_purge_children(self, parent, before=inf, cluster=0):
260 c915d3bf Antony Chazapis
        """Delete all versions with the specified
261 c915d3bf Antony Chazapis
           parent and cluster, and return
262 813e42e5 Antony Chazapis
           the hashes and size of versions deleted.
263 c915d3bf Antony Chazapis
           Clears out nodes with no remaining versions.
264 c915d3bf Antony Chazapis
        """
265 2715ade4 Sofia Papagiannaki
266 c915d3bf Antony Chazapis
        execute = self.execute
267 c915d3bf Antony Chazapis
        q = ("select count(serial), sum(size) from versions "
268 c915d3bf Antony Chazapis
             "where node in (select node "
269 2715ade4 Sofia Papagiannaki
             "from nodes "
270 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
271 c915d3bf Antony Chazapis
             "and cluster = ? "
272 c915d3bf Antony Chazapis
             "and mtime <= ?")
273 c915d3bf Antony Chazapis
        args = (parent, cluster, before)
274 c915d3bf Antony Chazapis
        execute(q, args)
275 c915d3bf Antony Chazapis
        nr, size = self.fetchone()
276 c915d3bf Antony Chazapis
        if not nr:
277 442bc80c Sofia Papagiannaki
            return (), 0, ()
278 60b8a083 Antony Chazapis
        mtime = time()
279 60b8a083 Antony Chazapis
        self.statistics_update(parent, -nr, -size, mtime, cluster)
280 60b8a083 Antony Chazapis
        self.statistics_update_ancestors(parent, -nr, -size, mtime, cluster)
281 2715ade4 Sofia Papagiannaki
282 e1f09a6e Antony Chazapis
        q = ("select hash from versions "
283 c915d3bf Antony Chazapis
             "where node in (select node "
284 2715ade4 Sofia Papagiannaki
             "from nodes "
285 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
286 c915d3bf Antony Chazapis
             "and cluster = ? "
287 c915d3bf Antony Chazapis
             "and mtime <= ?")
288 c915d3bf Antony Chazapis
        execute(q, args)
289 388ea25f Sofia Papagiannaki
        hashes = []
290 388ea25f Sofia Papagiannaki
        serials = []
291 388ea25f Sofia Papagiannaki
        for r in self.fetchall():
292 388ea25f Sofia Papagiannaki
            hashes += [r[0]]
293 388ea25f Sofia Papagiannaki
            serials += [r[1]]
294 388ea25f Sofia Papagiannaki
        
295 c915d3bf Antony Chazapis
        q = ("delete from versions "
296 c915d3bf Antony Chazapis
             "where node in (select node "
297 2715ade4 Sofia Papagiannaki
             "from nodes "
298 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
299 c915d3bf Antony Chazapis
             "and cluster = ? "
300 c915d3bf Antony Chazapis
             "and mtime <= ?")
301 c915d3bf Antony Chazapis
        execute(q, args)
302 62f915a1 Antony Chazapis
        q = ("delete from nodes "
303 62f915a1 Antony Chazapis
             "where node in (select node from nodes n "
304 2715ade4 Sofia Papagiannaki
             "where (select count(serial) "
305 2715ade4 Sofia Papagiannaki
             "from versions "
306 2715ade4 Sofia Papagiannaki
             "where node = n.node) = 0 "
307 2715ade4 Sofia Papagiannaki
             "and parent = ?)")
308 62f915a1 Antony Chazapis
        execute(q, (parent,))
309 388ea25f Sofia Papagiannaki
        return hashes, size, serials
310 2715ade4 Sofia Papagiannaki
311 c915d3bf Antony Chazapis
    def node_purge(self, node, before=inf, cluster=0):
312 c915d3bf Antony Chazapis
        """Delete all versions with the specified
313 c915d3bf Antony Chazapis
           node and cluster, and return
314 813e42e5 Antony Chazapis
           the hashes and size of versions deleted.
315 c915d3bf Antony Chazapis
           Clears out the node if it has no remaining versions.
316 c915d3bf Antony Chazapis
        """
317 2715ade4 Sofia Papagiannaki
318 c915d3bf Antony Chazapis
        execute = self.execute
319 c915d3bf Antony Chazapis
        q = ("select count(serial), sum(size) from versions "
320 c915d3bf Antony Chazapis
             "where node = ? "
321 c915d3bf Antony Chazapis
             "and cluster = ? "
322 c915d3bf Antony Chazapis
             "and mtime <= ?")
323 c915d3bf Antony Chazapis
        args = (node, cluster, before)
324 c915d3bf Antony Chazapis
        execute(q, args)
325 c915d3bf Antony Chazapis
        nr, size = self.fetchone()
326 c915d3bf Antony Chazapis
        if not nr:
327 442bc80c Sofia Papagiannaki
            return (), 0, ()
328 60b8a083 Antony Chazapis
        mtime = time()
329 60b8a083 Antony Chazapis
        self.statistics_update_ancestors(node, -nr, -size, mtime, cluster)
330 2715ade4 Sofia Papagiannaki
331 e1f09a6e Antony Chazapis
        q = ("select hash from versions "
332 c915d3bf Antony Chazapis
             "where node = ? "
333 c915d3bf Antony Chazapis
             "and cluster = ? "
334 c915d3bf Antony Chazapis
             "and mtime <= ?")
335 c915d3bf Antony Chazapis
        execute(q, args)
336 388ea25f Sofia Papagiannaki
        hashes = []
337 388ea25f Sofia Papagiannaki
        serials = []
338 388ea25f Sofia Papagiannaki
        for r in self.fetchall():
339 388ea25f Sofia Papagiannaki
            hashes += [r[0]]
340 388ea25f Sofia Papagiannaki
            serials += [r[1]]
341 388ea25f Sofia Papagiannaki
        
342 c915d3bf Antony Chazapis
        q = ("delete from versions "
343 c915d3bf Antony Chazapis
             "where node = ? "
344 c915d3bf Antony Chazapis
             "and cluster = ? "
345 c915d3bf Antony Chazapis
             "and mtime <= ?")
346 c915d3bf Antony Chazapis
        execute(q, args)
347 62f915a1 Antony Chazapis
        q = ("delete from nodes "
348 62f915a1 Antony Chazapis
             "where node in (select node from nodes n "
349 2715ade4 Sofia Papagiannaki
             "where (select count(serial) "
350 2715ade4 Sofia Papagiannaki
             "from versions "
351 2715ade4 Sofia Papagiannaki
             "where node = n.node) = 0 "
352 2715ade4 Sofia Papagiannaki
             "and node = ?)")
353 62f915a1 Antony Chazapis
        execute(q, (node,))
354 388ea25f Sofia Papagiannaki
        return hashes, size, serials
355 2715ade4 Sofia Papagiannaki
356 c915d3bf Antony Chazapis
    def node_remove(self, node):
357 c915d3bf Antony Chazapis
        """Remove the node specified.
358 c915d3bf Antony Chazapis
           Return false if the node has children or is not found.
359 c915d3bf Antony Chazapis
        """
360 2715ade4 Sofia Papagiannaki
361 c1cdc455 Antony Chazapis
        if self.node_count_children(node):
362 c915d3bf Antony Chazapis
            return False
363 2715ade4 Sofia Papagiannaki
364 c915d3bf Antony Chazapis
        mtime = time()
365 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), cluster "
366 c1cdc455 Antony Chazapis
             "from versions "
367 c1cdc455 Antony Chazapis
             "where node = ? "
368 c1cdc455 Antony Chazapis
             "group by cluster")
369 c915d3bf Antony Chazapis
        self.execute(q, (node,))
370 c915d3bf Antony Chazapis
        for population, size, cluster in self.fetchall():
371 2715ade4 Sofia Papagiannaki
            self.statistics_update_ancestors(
372 2715ade4 Sofia Papagiannaki
                node, -population, -size, mtime, cluster)
373 2715ade4 Sofia Papagiannaki
374 c915d3bf Antony Chazapis
        q = "delete from nodes where node = ?"
375 c915d3bf Antony Chazapis
        self.execute(q, (node,))
376 c915d3bf Antony Chazapis
        return True
377 2715ade4 Sofia Papagiannaki
378 5e7485da Antony Chazapis
    def policy_get(self, node):
379 5e7485da Antony Chazapis
        q = "select key, value from policy where node = ?"
380 5e7485da Antony Chazapis
        self.execute(q, (node,))
381 5e7485da Antony Chazapis
        return dict(self.fetchall())
382 2715ade4 Sofia Papagiannaki
383 5e7485da Antony Chazapis
    def policy_set(self, node, policy):
384 5e7485da Antony Chazapis
        q = "insert or replace into policy (node, key, value) values (?, ?, ?)"
385 5e7485da Antony Chazapis
        self.executemany(q, ((node, k, v) for k, v in policy.iteritems()))
386 2715ade4 Sofia Papagiannaki
387 c1cdc455 Antony Chazapis
    def statistics_get(self, node, cluster=0):
388 c1cdc455 Antony Chazapis
        """Return population, total size and last mtime
389 c1cdc455 Antony Chazapis
           for all versions under node that belong to the cluster.
390 c1cdc455 Antony Chazapis
        """
391 2715ade4 Sofia Papagiannaki
392 62f915a1 Antony Chazapis
        q = ("select population, size, mtime from statistics "
393 c1cdc455 Antony Chazapis
             "where node = ? and cluster = ?")
394 c1cdc455 Antony Chazapis
        self.execute(q, (node, cluster))
395 62f915a1 Antony Chazapis
        return self.fetchone()
396 2715ade4 Sofia Papagiannaki
397 c1cdc455 Antony Chazapis
    def statistics_update(self, node, population, size, mtime, cluster=0):
398 c1cdc455 Antony Chazapis
        """Update the statistics of the given node.
399 c1cdc455 Antony Chazapis
           Statistics keep track the population, total
400 c1cdc455 Antony Chazapis
           size of objects and mtime in the node's namespace.
401 c1cdc455 Antony Chazapis
           May be zero or positive or negative numbers.
402 c1cdc455 Antony Chazapis
        """
403 2715ade4 Sofia Papagiannaki
404 62f915a1 Antony Chazapis
        qs = ("select population, size from statistics "
405 c1cdc455 Antony Chazapis
              "where node = ? and cluster = ?")
406 c1cdc455 Antony Chazapis
        qu = ("insert or replace into statistics (node, population, size, mtime, cluster) "
407 c1cdc455 Antony Chazapis
              "values (?, ?, ?, ?, ?)")
408 c1cdc455 Antony Chazapis
        self.execute(qs, (node, cluster))
409 c1cdc455 Antony Chazapis
        r = self.fetchone()
410 c1cdc455 Antony Chazapis
        if r is None:
411 c1cdc455 Antony Chazapis
            prepopulation, presize = (0, 0)
412 c1cdc455 Antony Chazapis
        else:
413 c1cdc455 Antony Chazapis
            prepopulation, presize = r
414 c1cdc455 Antony Chazapis
        population += prepopulation
415 096a7c3b Sofia Papagiannaki
        population = max(population, 0)
416 c1cdc455 Antony Chazapis
        size += presize
417 c1cdc455 Antony Chazapis
        self.execute(qu, (node, population, size, mtime, cluster))
418 2715ade4 Sofia Papagiannaki
419 c1cdc455 Antony Chazapis
    def statistics_update_ancestors(self, node, population, size, mtime, cluster=0):
420 c1cdc455 Antony Chazapis
        """Update the statistics of the given node's parent.
421 c1cdc455 Antony Chazapis
           Then recursively update all parents up to the root.
422 c1cdc455 Antony Chazapis
           Population is not recursive.
423 c1cdc455 Antony Chazapis
        """
424 2715ade4 Sofia Papagiannaki
425 c1cdc455 Antony Chazapis
        while True:
426 c1cdc455 Antony Chazapis
            if node == 0:
427 c1cdc455 Antony Chazapis
                break
428 62f915a1 Antony Chazapis
            props = self.node_get_properties(node)
429 62f915a1 Antony Chazapis
            if props is None:
430 c1cdc455 Antony Chazapis
                break
431 62f915a1 Antony Chazapis
            parent, path = props
432 62f915a1 Antony Chazapis
            self.statistics_update(parent, population, size, mtime, cluster)
433 62f915a1 Antony Chazapis
            node = parent
434 2715ade4 Sofia Papagiannaki
            population = 0  # Population isn't recursive
435 2715ade4 Sofia Papagiannaki
436 62f915a1 Antony Chazapis
    def statistics_latest(self, node, before=inf, except_cluster=0):
437 62f915a1 Antony Chazapis
        """Return population, total size and last mtime
438 62f915a1 Antony Chazapis
           for all latest versions under node that
439 62f915a1 Antony Chazapis
           do not belong to the cluster.
440 62f915a1 Antony Chazapis
        """
441 2715ade4 Sofia Papagiannaki
442 676edf89 Antony Chazapis
        execute = self.execute
443 676edf89 Antony Chazapis
        fetchone = self.fetchone
444 2715ade4 Sofia Papagiannaki
445 62f915a1 Antony Chazapis
        # The node.
446 62f915a1 Antony Chazapis
        props = self.node_get_properties(node)
447 62f915a1 Antony Chazapis
        if props is None:
448 62f915a1 Antony Chazapis
            return None
449 62f915a1 Antony Chazapis
        parent, path = props
450 2715ade4 Sofia Papagiannaki
451 62f915a1 Antony Chazapis
        # The latest version.
452 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
453 585b75e7 Sofia Papagiannaki
             "from versions v "
454 585b75e7 Sofia Papagiannaki
             "where serial = %s "
455 62f915a1 Antony Chazapis
             "and cluster != ?")
456 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
457 2715ade4 Sofia Papagiannaki
            node=node, before=before)
458 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster])
459 676edf89 Antony Chazapis
        props = fetchone()
460 62f915a1 Antony Chazapis
        if props is None:
461 62f915a1 Antony Chazapis
            return None
462 62f915a1 Antony Chazapis
        mtime = props[MTIME]
463 2715ade4 Sofia Papagiannaki
464 62f915a1 Antony Chazapis
        # First level, just under node (get population).
465 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
466 62f915a1 Antony Chazapis
             "from versions v "
467 585b75e7 Sofia Papagiannaki
             "where serial = %s "
468 62f915a1 Antony Chazapis
             "and cluster != ? "
469 62f915a1 Antony Chazapis
             "and node in (select node "
470 2715ade4 Sofia Papagiannaki
             "from nodes "
471 2715ade4 Sofia Papagiannaki
             "where parent = ?)")
472 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
473 2715ade4 Sofia Papagiannaki
            node=None, before=before)
474 585b75e7 Sofia Papagiannaki
        execute(q % subq, args + [except_cluster, node])
475 62f915a1 Antony Chazapis
        r = fetchone()
476 62f915a1 Antony Chazapis
        if r is None:
477 62f915a1 Antony Chazapis
            return None
478 62f915a1 Antony Chazapis
        count = r[0]
479 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
480 62f915a1 Antony Chazapis
        if count == 0:
481 62f915a1 Antony Chazapis
            return (0, 0, mtime)
482 2715ade4 Sofia Papagiannaki
483 62f915a1 Antony Chazapis
        # All children (get size and mtime).
484 92da0e5a Antony Chazapis
        # This is why the full path is stored.
485 62f915a1 Antony Chazapis
        q = ("select count(serial), sum(size), max(mtime) "
486 62f915a1 Antony Chazapis
             "from versions v "
487 585b75e7 Sofia Papagiannaki
             "where serial = %s "
488 62f915a1 Antony Chazapis
             "and cluster != ? "
489 62f915a1 Antony Chazapis
             "and node in (select node "
490 2715ade4 Sofia Papagiannaki
             "from nodes "
491 2715ade4 Sofia Papagiannaki
             "where path like ? escape '\\')")
492 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
493 2715ade4 Sofia Papagiannaki
            node=None, before=before)
494 2715ade4 Sofia Papagiannaki
        execute(
495 2715ade4 Sofia Papagiannaki
            q % subq, args + [except_cluster, self.escape_like(path) + '%'])
496 62f915a1 Antony Chazapis
        r = fetchone()
497 62f915a1 Antony Chazapis
        if r is None:
498 62f915a1 Antony Chazapis
            return None
499 62f915a1 Antony Chazapis
        size = r[1] - props[SIZE]
500 62f915a1 Antony Chazapis
        mtime = max(mtime, r[2])
501 62f915a1 Antony Chazapis
        return (count, size, mtime)
502 2715ade4 Sofia Papagiannaki
503 585b75e7 Sofia Papagiannaki
    def nodes_set_latest_version(self, node, serial):
504 2715ade4 Sofia Papagiannaki
        q = ("update nodes set latest_version = ? where node = ?")
505 585b75e7 Sofia Papagiannaki
        props = (serial, node)
506 585b75e7 Sofia Papagiannaki
        self.execute(q, props)
507 2715ade4 Sofia Papagiannaki
508 33b4e4a6 Antony Chazapis
    def version_create(self, node, hash, size, type, source, muser, uuid, checksum, cluster=0):
509 44ad5860 Antony Chazapis
        """Create a new version from the given properties.
510 44ad5860 Antony Chazapis
           Return the (serial, mtime) of the new version.
511 44ad5860 Antony Chazapis
        """
512 2715ade4 Sofia Papagiannaki
513 33b4e4a6 Antony Chazapis
        q = ("insert into versions (node, hash, size, type, source, mtime, muser, uuid, checksum, cluster) "
514 33b4e4a6 Antony Chazapis
             "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
515 44ad5860 Antony Chazapis
        mtime = time()
516 2715ade4 Sofia Papagiannaki
        props = (node, hash, size, type, source, mtime, muser,
517 2715ade4 Sofia Papagiannaki
                 uuid, checksum, cluster)
518 44ad5860 Antony Chazapis
        serial = self.execute(q, props).lastrowid
519 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
520 2715ade4 Sofia Papagiannaki
521 585b75e7 Sofia Papagiannaki
        self.nodes_set_latest_version(node, serial)
522 2715ade4 Sofia Papagiannaki
523 44ad5860 Antony Chazapis
        return serial, mtime
524 2715ade4 Sofia Papagiannaki
525 cf4a7a7b Sofia Papagiannaki
    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
526 44ad5860 Antony Chazapis
        """Lookup the current version of the given node.
527 44ad5860 Antony Chazapis
           Return a list with its properties:
528 2715ade4 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime,
529 2715ade4 Sofia Papagiannaki
            muser, uuid, checksum, cluster)
530 44ad5860 Antony Chazapis
           or None if the current version is not found in the given cluster.
531 44ad5860 Antony Chazapis
        """
532 2715ade4 Sofia Papagiannaki
533 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
534 585b75e7 Sofia Papagiannaki
             "from versions v "
535 585b75e7 Sofia Papagiannaki
             "where serial = %s "
536 44ad5860 Antony Chazapis
             "and cluster = ?")
537 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_version_subquery(
538 2715ade4 Sofia Papagiannaki
            node=node, before=before)
539 cf4a7a7b Sofia Papagiannaki
        if not all_props:
540 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq)
541 cf4a7a7b Sofia Papagiannaki
        else:
542 585b75e7 Sofia Papagiannaki
            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster", subq)
543 2715ade4 Sofia Papagiannaki
544 585b75e7 Sofia Papagiannaki
        self.execute(q, args + [cluster])
545 44ad5860 Antony Chazapis
        props = self.fetchone()
546 44ad5860 Antony Chazapis
        if props is not None:
547 44ad5860 Antony Chazapis
            return props
548 44ad5860 Antony Chazapis
        return None
549 4d15c94e Sofia Papagiannaki
550 cf4a7a7b Sofia Papagiannaki
    def version_lookup_bulk(self, nodes, before=inf, cluster=0, all_props=True):
551 4d15c94e Sofia Papagiannaki
        """Lookup the current versions of the given nodes.
552 4d15c94e Sofia Papagiannaki
           Return a list with their properties:
553 4d15c94e Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
554 4d15c94e Sofia Papagiannaki
        """
555 2715ade4 Sofia Papagiannaki
556 585b75e7 Sofia Papagiannaki
        if not nodes:
557 2715ade4 Sofia Papagiannaki
            return ()
558 cf4a7a7b Sofia Papagiannaki
        q = ("select %s "
559 4d15c94e Sofia Papagiannaki
             "from versions "
560 585b75e7 Sofia Papagiannaki
             "where serial in %s "
561 9ac201e2 Sofia Papagiannaki
             "and cluster = ? %s")
562 2715ade4 Sofia Papagiannaki
        subq, args = self._construct_latest_versions_subquery(
563 2715ade4 Sofia Papagiannaki
            nodes=nodes, before=before)
564 cf4a7a7b Sofia Papagiannaki
        if not all_props:
565 585b75e7 Sofia Papagiannaki
            q = q % ("serial", subq, '')
566 cf4a7a7b Sofia Papagiannaki
        else:
567 2715ade4 Sofia Papagiannaki
            q = q % ("serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster", subq, 'order by node')
568 2715ade4 Sofia Papagiannaki
569 585b75e7 Sofia Papagiannaki
        args += [cluster]
570 4d15c94e Sofia Papagiannaki
        self.execute(q, args)
571 4d15c94e Sofia Papagiannaki
        return self.fetchall()
572 2715ade4 Sofia Papagiannaki
573 1f2d1b42 Antony Chazapis
    def version_get_properties(self, serial, keys=(), propnames=_propnames):
574 1f2d1b42 Antony Chazapis
        """Return a sequence of values for the properties of
575 1f2d1b42 Antony Chazapis
           the version specified by serial and the keys, in the order given.
576 1f2d1b42 Antony Chazapis
           If keys is empty, return all properties in the order
577 33b4e4a6 Antony Chazapis
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
578 1f2d1b42 Antony Chazapis
        """
579 2715ade4 Sofia Papagiannaki
580 33b4e4a6 Antony Chazapis
        q = ("select serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster "
581 62f915a1 Antony Chazapis
             "from versions "
582 1f2d1b42 Antony Chazapis
             "where serial = ?")
583 1f2d1b42 Antony Chazapis
        self.execute(q, (serial,))
584 1f2d1b42 Antony Chazapis
        r = self.fetchone()
585 1f2d1b42 Antony Chazapis
        if r is None:
586 1f2d1b42 Antony Chazapis
            return r
587 2715ade4 Sofia Papagiannaki
588 1f2d1b42 Antony Chazapis
        if not keys:
589 1f2d1b42 Antony Chazapis
            return r
590 1f2d1b42 Antony Chazapis
        return [r[propnames[k]] for k in keys if k in propnames]
591 2715ade4 Sofia Papagiannaki
592 33b4e4a6 Antony Chazapis
    def version_put_property(self, serial, key, value):
593 33b4e4a6 Antony Chazapis
        """Set value for the property of version specified by key."""
594 2715ade4 Sofia Papagiannaki
595 33b4e4a6 Antony Chazapis
        if key not in _propnames:
596 33b4e4a6 Antony Chazapis
            return
597 33b4e4a6 Antony Chazapis
        q = "update versions set %s = ? where serial = ?" % key
598 33b4e4a6 Antony Chazapis
        self.execute(q, (value, serial))
599 2715ade4 Sofia Papagiannaki
600 44ad5860 Antony Chazapis
    def version_recluster(self, serial, cluster):
601 44ad5860 Antony Chazapis
        """Move the version into another cluster."""
602 2715ade4 Sofia Papagiannaki
603 62f915a1 Antony Chazapis
        props = self.version_get_properties(serial)
604 62f915a1 Antony Chazapis
        if not props:
605 62f915a1 Antony Chazapis
            return
606 44ad5860 Antony Chazapis
        node = props[NODE]
607 44ad5860 Antony Chazapis
        size = props[SIZE]
608 44ad5860 Antony Chazapis
        oldcluster = props[CLUSTER]
609 44ad5860 Antony Chazapis
        if cluster == oldcluster:
610 44ad5860 Antony Chazapis
            return
611 2715ade4 Sofia Papagiannaki
612 62f915a1 Antony Chazapis
        mtime = time()
613 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster)
614 c1cdc455 Antony Chazapis
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
615 2715ade4 Sofia Papagiannaki
616 62f915a1 Antony Chazapis
        q = "update versions set cluster = ? where serial = ?"
617 c915d3bf Antony Chazapis
        self.execute(q, (cluster, serial))
618 2715ade4 Sofia Papagiannaki
619 62f915a1 Antony Chazapis
    def version_remove(self, serial):
620 62f915a1 Antony Chazapis
        """Remove the serial specified."""
621 2715ade4 Sofia Papagiannaki
622 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
623 62f915a1 Antony Chazapis
        if not props:
624 62f915a1 Antony Chazapis
            return
625 62f915a1 Antony Chazapis
        node = props[NODE]
626 5161c672 Antony Chazapis
        hash = props[HASH]
627 62f915a1 Antony Chazapis
        size = props[SIZE]
628 62f915a1 Antony Chazapis
        cluster = props[CLUSTER]
629 2715ade4 Sofia Papagiannaki
630 62f915a1 Antony Chazapis
        mtime = time()
631 62f915a1 Antony Chazapis
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster)
632 2715ade4 Sofia Papagiannaki
633 62f915a1 Antony Chazapis
        q = "delete from versions where serial = ?"
634 62f915a1 Antony Chazapis
        self.execute(q, (serial,))
635 2715ade4 Sofia Papagiannaki
636 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
637 585b75e7 Sofia Papagiannaki
        if props:
638 2715ade4 Sofia Papagiannaki
            self.nodes_set_latest_version(node, props[0])
639 813e42e5 Antony Chazapis
        return hash, size
640 2715ade4 Sofia Papagiannaki
641 4819d34f Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
642 44ad5860 Antony Chazapis
        """Return a list of (key, value) pairs of the version specified by serial.
643 1f2d1b42 Antony Chazapis
           If keys is empty, return all attributes.
644 1f2d1b42 Antony Chazapis
           Othwerise, return only those specified.
645 1f2d1b42 Antony Chazapis
        """
646 2715ade4 Sofia Papagiannaki
647 1f2d1b42 Antony Chazapis
        execute = self.execute
648 1f2d1b42 Antony Chazapis
        if keys:
649 1f2d1b42 Antony Chazapis
            marks = ','.join('?' for k in keys)
650 1f2d1b42 Antony Chazapis
            q = ("select key, value from attributes "
651 4819d34f Antony Chazapis
                 "where key in (%s) and serial = ? and domain = ?" % (marks,))
652 4819d34f Antony Chazapis
            execute(q, keys + (serial, domain))
653 1f2d1b42 Antony Chazapis
        else:
654 059857e2 Antony Chazapis
            q = "select key, value from attributes where serial = ? and domain = ?"
655 059857e2 Antony Chazapis
            execute(q, (serial, domain))
656 1f2d1b42 Antony Chazapis
        return self.fetchall()
657 2715ade4 Sofia Papagiannaki
658 4819d34f Antony Chazapis
    def attribute_set(self, serial, domain, items):
659 44ad5860 Antony Chazapis
        """Set the attributes of the version specified by serial.
660 1f2d1b42 Antony Chazapis
           Receive attributes as an iterable of (key, value) pairs.
661 1f2d1b42 Antony Chazapis
        """
662 2715ade4 Sofia Papagiannaki
663 4819d34f Antony Chazapis
        q = ("insert or replace into attributes (serial, domain, key, value) "
664 4819d34f Antony Chazapis
             "values (?, ?, ?, ?)")
665 4819d34f Antony Chazapis
        self.executemany(q, ((serial, domain, k, v) for k, v in items))
666 2715ade4 Sofia Papagiannaki
667 4819d34f Antony Chazapis
    def attribute_del(self, serial, domain, keys=()):
668 44ad5860 Antony Chazapis
        """Delete attributes of the version specified by serial.
669 1f2d1b42 Antony Chazapis
           If keys is empty, delete all attributes.
670 1f2d1b42 Antony Chazapis
           Otherwise delete those specified.
671 1f2d1b42 Antony Chazapis
        """
672 2715ade4 Sofia Papagiannaki
673 1f2d1b42 Antony Chazapis
        if keys:
674 4819d34f Antony Chazapis
            q = "delete from attributes where serial = ? and domain = ? and key = ?"
675 4819d34f Antony Chazapis
            self.executemany(q, ((serial, domain, key) for key in keys))
676 1f2d1b42 Antony Chazapis
        else:
677 4819d34f Antony Chazapis
            q = "delete from attributes where serial = ? and domain = ?"
678 4819d34f Antony Chazapis
            self.execute(q, (serial, domain))
679 2715ade4 Sofia Papagiannaki
680 44ad5860 Antony Chazapis
    def attribute_copy(self, source, dest):
681 1f2d1b42 Antony Chazapis
        q = ("insert or replace into attributes "
682 4819d34f Antony Chazapis
             "select ?, domain, key, value from attributes "
683 1f2d1b42 Antony Chazapis
             "where serial = ?")
684 1f2d1b42 Antony Chazapis
        self.execute(q, (dest, source))
685 2715ade4 Sofia Papagiannaki
686 4819d34f Antony Chazapis
    def _construct_filters(self, domain, filterq):
687 4819d34f Antony Chazapis
        if not domain or not filterq:
688 60b8a083 Antony Chazapis
            return None, None
689 2715ade4 Sofia Papagiannaki
690 95d47e1a Antony Chazapis
        subqlist = []
691 95d47e1a Antony Chazapis
        append = subqlist.append
692 4819d34f Antony Chazapis
        included, excluded, opers = parse_filters(filterq)
693 95d47e1a Antony Chazapis
        args = []
694 2715ade4 Sofia Papagiannaki
695 95d47e1a Antony Chazapis
        if included:
696 c7c0790f chazapis
            subq = "exists (select 1 from attributes where serial = v.serial and domain = ? and "
697 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in included)) + ")"
698 c7c0790f chazapis
            subq += ")"
699 c7c0790f chazapis
            args += [domain]
700 95d47e1a Antony Chazapis
            args += included
701 95d47e1a Antony Chazapis
            append(subq)
702 2715ade4 Sofia Papagiannaki
703 95d47e1a Antony Chazapis
        if excluded:
704 c7c0790f chazapis
            subq = "not exists (select 1 from attributes where serial = v.serial and domain = ? and "
705 c7c0790f chazapis
            subq += "(" + ' or '.join(('key = ?' for x in excluded)) + ")"
706 c7c0790f chazapis
            subq += ")"
707 c7c0790f chazapis
            args += [domain]
708 95d47e1a Antony Chazapis
            args += excluded
709 95d47e1a Antony Chazapis
            append(subq)
710 2715ade4 Sofia Papagiannaki
711 95d47e1a Antony Chazapis
        if opers:
712 95d47e1a Antony Chazapis
            for k, o, v in opers:
713 6b2f11b4 Antony Chazapis
                subq = "exists (select 1 from attributes where serial = v.serial and domain = ? and "
714 6b2f11b4 Antony Chazapis
                subq += "key = ? and value %s ?" % (o,)
715 6b2f11b4 Antony Chazapis
                subq += ")"
716 6b2f11b4 Antony Chazapis
                args += [domain, k, v]
717 6b2f11b4 Antony Chazapis
                append(subq)
718 2715ade4 Sofia Papagiannaki
719 95d47e1a Antony Chazapis
        if not subqlist:
720 95d47e1a Antony Chazapis
            return None, None
721 2715ade4 Sofia Papagiannaki
722 c7c0790f chazapis
        subq = ' and ' + ' and '.join(subqlist)
723 2715ade4 Sofia Papagiannaki
724 60b8a083 Antony Chazapis
        return subq, args
725 2715ade4 Sofia Papagiannaki
726 60b8a083 Antony Chazapis
    def _construct_paths(self, pathq):
727 60b8a083 Antony Chazapis
        if not pathq:
728 60b8a083 Antony Chazapis
            return None, None
729 2715ade4 Sofia Papagiannaki
730 d57eaad4 Antony Chazapis
        subqlist = []
731 d57eaad4 Antony Chazapis
        args = []
732 d57eaad4 Antony Chazapis
        for path, match in pathq:
733 d57eaad4 Antony Chazapis
            if match == MATCH_PREFIX:
734 d57eaad4 Antony Chazapis
                subqlist.append("n.path like ? escape '\\'")
735 d57eaad4 Antony Chazapis
                args.append(self.escape_like(path) + '%')
736 d57eaad4 Antony Chazapis
            elif match == MATCH_EXACT:
737 d57eaad4 Antony Chazapis
                subqlist.append("n.path = ?")
738 d57eaad4 Antony Chazapis
                args.append(path)
739 2715ade4 Sofia Papagiannaki
740 d57eaad4 Antony Chazapis
        subq = ' and (' + ' or '.join(subqlist) + ')'
741 d57eaad4 Antony Chazapis
        args = tuple(args)
742 2715ade4 Sofia Papagiannaki
743 60b8a083 Antony Chazapis
        return subq, args
744 2715ade4 Sofia Papagiannaki
745 7ff57991 Antony Chazapis
    def _construct_size(self, sizeq):
746 7ff57991 Antony Chazapis
        if not sizeq or len(sizeq) != 2:
747 7ff57991 Antony Chazapis
            return None, None
748 2715ade4 Sofia Papagiannaki
749 7ff57991 Antony Chazapis
        subq = ''
750 7ff57991 Antony Chazapis
        args = []
751 7ff57991 Antony Chazapis
        if sizeq[0]:
752 7ff57991 Antony Chazapis
            subq += " and v.size >= ?"
753 7ff57991 Antony Chazapis
            args += [sizeq[0]]
754 7ff57991 Antony Chazapis
        if sizeq[1]:
755 7ff57991 Antony Chazapis
            subq += " and v.size < ?"
756 7ff57991 Antony Chazapis
            args += [sizeq[1]]
757 2715ade4 Sofia Papagiannaki
758 7ff57991 Antony Chazapis
        return subq, args
759 2715ade4 Sofia Papagiannaki
760 585b75e7 Sofia Papagiannaki
    def _construct_versions_nodes_latest_version_subquery(self, before=inf):
761 585b75e7 Sofia Papagiannaki
        if before == inf:
762 585b75e7 Sofia Papagiannaki
            q = ("n.latest_version ")
763 585b75e7 Sofia Papagiannaki
            args = []
764 585b75e7 Sofia Papagiannaki
        else:
765 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
766 2715ade4 Sofia Papagiannaki
                 "from versions "
767 2715ade4 Sofia Papagiannaki
                 "where node = v.node and mtime < ?) ")
768 585b75e7 Sofia Papagiannaki
            args = [before]
769 585b75e7 Sofia Papagiannaki
        return q, args
770 2715ade4 Sofia Papagiannaki
771 585b75e7 Sofia Papagiannaki
    def _construct_latest_version_subquery(self, node=None, before=inf):
772 585b75e7 Sofia Papagiannaki
        where_cond = "node = v.node"
773 585b75e7 Sofia Papagiannaki
        args = []
774 585b75e7 Sofia Papagiannaki
        if node:
775 585b75e7 Sofia Papagiannaki
            where_cond = "node = ? "
776 585b75e7 Sofia Papagiannaki
            args = [node]
777 2715ade4 Sofia Papagiannaki
778 585b75e7 Sofia Papagiannaki
        if before == inf:
779 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
780 2715ade4 Sofia Papagiannaki
                 "from nodes "
781 2715ade4 Sofia Papagiannaki
                 "where %s) ")
782 585b75e7 Sofia Papagiannaki
        else:
783 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
784 2715ade4 Sofia Papagiannaki
                 "from versions "
785 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ?) ")
786 585b75e7 Sofia Papagiannaki
            args += [before]
787 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
788 2715ade4 Sofia Papagiannaki
789 585b75e7 Sofia Papagiannaki
    def _construct_latest_versions_subquery(self, nodes=(), before=inf):
790 585b75e7 Sofia Papagiannaki
        where_cond = ""
791 585b75e7 Sofia Papagiannaki
        args = []
792 585b75e7 Sofia Papagiannaki
        if nodes:
793 585b75e7 Sofia Papagiannaki
            where_cond = "node in (%s) " % ','.join('?' for node in nodes)
794 585b75e7 Sofia Papagiannaki
            args = nodes
795 2715ade4 Sofia Papagiannaki
796 585b75e7 Sofia Papagiannaki
        if before == inf:
797 585b75e7 Sofia Papagiannaki
            q = ("(select latest_version "
798 2715ade4 Sofia Papagiannaki
                 "from nodes "
799 2715ade4 Sofia Papagiannaki
                 "where %s ) ")
800 585b75e7 Sofia Papagiannaki
        else:
801 585b75e7 Sofia Papagiannaki
            q = ("(select max(serial) "
802 2715ade4 Sofia Papagiannaki
                 "from versions "
803 2715ade4 Sofia Papagiannaki
                 "where %s and mtime < ? group by node) ")
804 585b75e7 Sofia Papagiannaki
            args += [before]
805 585b75e7 Sofia Papagiannaki
        return q % where_cond, args
806 2715ade4 Sofia Papagiannaki
807 4819d34f Antony Chazapis
    def latest_attribute_keys(self, parent, domain, before=inf, except_cluster=0, pathq=[]):
808 676edf89 Antony Chazapis
        """Return a list with all keys pairs defined
809 676edf89 Antony Chazapis
           for all latest versions under parent that
810 676edf89 Antony Chazapis
           do not belong to the cluster.
811 676edf89 Antony Chazapis
        """
812 2715ade4 Sofia Papagiannaki
813 676edf89 Antony Chazapis
        # TODO: Use another table to store before=inf results.
814 60b8a083 Antony Chazapis
        q = ("select distinct a.key "
815 676edf89 Antony Chazapis
             "from attributes a, versions v, nodes n "
816 585b75e7 Sofia Papagiannaki
             "where v.serial = %s "
817 676edf89 Antony Chazapis
             "and v.cluster != ? "
818 676edf89 Antony Chazapis
             "and v.node in (select node "
819 2715ade4 Sofia Papagiannaki
             "from nodes "
820 2715ade4 Sofia Papagiannaki
             "where parent = ?) "
821 676edf89 Antony Chazapis
             "and a.serial = v.serial "
822 4819d34f Antony Chazapis
             "and a.domain = ? "
823 676edf89 Antony Chazapis
             "and n.node = v.node")
824 2715ade4 Sofia Papagiannaki
        subq, subargs = self._construct_latest_version_subquery(
825 2715ade4 Sofia Papagiannaki
            node=None, before=before)
826 585b75e7 Sofia Papagiannaki
        args = subargs + [except_cluster, parent, domain]
827 585b75e7 Sofia Papagiannaki
        q = q % subq
828 60b8a083 Antony Chazapis
        subq, subargs = self._construct_paths(pathq)
829 60b8a083 Antony Chazapis
        if subq is not None:
830 60b8a083 Antony Chazapis
            q += subq
831 60b8a083 Antony Chazapis
            args += subargs
832 676edf89 Antony Chazapis
        self.execute(q, args)
833 676edf89 Antony Chazapis
        return [r[0] for r in self.fetchall()]
834 2715ade4 Sofia Papagiannaki
835 60b8a083 Antony Chazapis
    def latest_version_list(self, parent, prefix='', delimiter=None,
836 60b8a083 Antony Chazapis
                            start='', limit=10000, before=inf,
837 371d907a Antony Chazapis
                            except_cluster=0, pathq=[], domain=None,
838 371d907a Antony Chazapis
                            filterq=[], sizeq=None, all_props=False):
839 60b8a083 Antony Chazapis
        """Return a (list of (path, serial) tuples, list of common prefixes)
840 60b8a083 Antony Chazapis
           for the current versions of the paths with the given parent,
841 60b8a083 Antony Chazapis
           matching the following criteria.
842 2715ade4 Sofia Papagiannaki

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

846 60b8a083 Antony Chazapis
                a. parent matches
847 2715ade4 Sofia Papagiannaki

848 60b8a083 Antony Chazapis
                b. path > start
849 2715ade4 Sofia Papagiannaki

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

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

854 60b8a083 Antony Chazapis
                e. version is not in cluster
855 2715ade4 Sofia Papagiannaki

856 60b8a083 Antony Chazapis
                f. the path does not have the delimiter occuring
857 60b8a083 Antony Chazapis
                   after the prefix, or ends with the delimiter
858 2715ade4 Sofia Papagiannaki

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

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

864 60b8a083 Antony Chazapis
                   key
865 60b8a083 Antony Chazapis
                       an attribute with this key must exist
866 2715ade4 Sofia Papagiannaki

867 60b8a083 Antony Chazapis
                   !key
868 60b8a083 Antony Chazapis
                       an attribute with this key must not exist
869 2715ade4 Sofia Papagiannaki

870 60b8a083 Antony Chazapis
                   key ?op value
871 60b8a083 Antony Chazapis
                       the attribute with this key satisfies the value
872 1713c946 Antony Chazapis
                       where ?op is one of =, != <=, >=, <, >.
873 2715ade4 Sofia Papagiannaki

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

876 60b8a083 Antony Chazapis
           The list of common prefixes includes the prefixes
877 60b8a083 Antony Chazapis
           matching up to the first delimiter after prefix,
878 60b8a083 Antony Chazapis
           and are reported only once, as "virtual directories".
879 60b8a083 Antony Chazapis
           The delimiter is included in the prefixes.
880 2715ade4 Sofia Papagiannaki

881 60b8a083 Antony Chazapis
           If arguments are None, then the corresponding matching rule
882 60b8a083 Antony Chazapis
           will always match.
883 2715ade4 Sofia Papagiannaki

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

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

978 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
979 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
980 2bbf1544 Georgios D. Tsoukalas

981 2bbf1544 Georgios D. Tsoukalas
        """
982 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
983 2bbf1544 Georgios D. Tsoukalas
            cluster_where = "and cluster = ?"
984 2bbf1544 Georgios D. Tsoukalas
            args = (uuid, int(cluster))
985 2bbf1544 Georgios D. Tsoukalas
        else:
986 2bbf1544 Georgios D. Tsoukalas
            cluster_where = ""
987 2bbf1544 Georgios D. Tsoukalas
            args = (uuid,)
988 2715ade4 Sofia Papagiannaki
989 37bee317 Antony Chazapis
        q = ("select n.path, v.serial "
990 37bee317 Antony Chazapis
             "from versions v, nodes n "
991 37bee317 Antony Chazapis
             "where v.serial = (select max(serial) "
992 2715ade4 Sofia Papagiannaki
             "from versions "
993 2bbf1544 Georgios D. Tsoukalas
             "where uuid = ? %s) "
994 2bbf1544 Georgios D. Tsoukalas
             "and n.node = v.node") % cluster_where
995 2bbf1544 Georgios D. Tsoukalas
        self.execute(q, args)
996 37bee317 Antony Chazapis
        return self.fetchone()