Statistics
| Branch: | Tag: | Revision:

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

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

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

835 60b8a083 Antony Chazapis
                a. parent matches
836 2715ade4 Sofia Papagiannaki

837 60b8a083 Antony Chazapis
                b. path > start
838 2715ade4 Sofia Papagiannaki

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

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

843 60b8a083 Antony Chazapis
                e. version is not in cluster
844 2715ade4 Sofia Papagiannaki

845 60b8a083 Antony Chazapis
                f. the path does not have the delimiter occuring
846 60b8a083 Antony Chazapis
                   after the prefix, or ends with the delimiter
847 2715ade4 Sofia Papagiannaki

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

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

853 60b8a083 Antony Chazapis
                   key
854 60b8a083 Antony Chazapis
                       an attribute with this key must exist
855 2715ade4 Sofia Papagiannaki

856 60b8a083 Antony Chazapis
                   !key
857 60b8a083 Antony Chazapis
                       an attribute with this key must not exist
858 2715ade4 Sofia Papagiannaki

859 60b8a083 Antony Chazapis
                   key ?op value
860 60b8a083 Antony Chazapis
                       the attribute with this key satisfies the value
861 1713c946 Antony Chazapis
                       where ?op is one of =, != <=, >=, <, >.
862 2715ade4 Sofia Papagiannaki

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

865 60b8a083 Antony Chazapis
           The list of common prefixes includes the prefixes
866 60b8a083 Antony Chazapis
           matching up to the first delimiter after prefix,
867 60b8a083 Antony Chazapis
           and are reported only once, as "virtual directories".
868 60b8a083 Antony Chazapis
           The delimiter is included in the prefixes.
869 2715ade4 Sofia Papagiannaki

870 60b8a083 Antony Chazapis
           If arguments are None, then the corresponding matching rule
871 60b8a083 Antony Chazapis
           will always match.
872 2715ade4 Sofia Papagiannaki

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

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