Statistics
| Branch: | Tag: | Revision:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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