Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / lib / sqlalchemy / node.py @ 6e3e5c84

History | View | Annotate | Download (49.7 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 2715ade4 Sofia Papagiannaki
#
3 4f917833 Sofia Papagiannaki
# Redistribution and use in source and binary forms, with or
4 4f917833 Sofia Papagiannaki
# without modification, are permitted provided that the following
5 4f917833 Sofia Papagiannaki
# conditions are met:
6 2715ade4 Sofia Papagiannaki
#
7 4f917833 Sofia Papagiannaki
#   1. Redistributions of source code must retain the above
8 4f917833 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
9 4f917833 Sofia Papagiannaki
#      disclaimer.
10 2715ade4 Sofia Papagiannaki
#
11 4f917833 Sofia Papagiannaki
#   2. Redistributions in binary form must reproduce the above
12 4f917833 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
13 4f917833 Sofia Papagiannaki
#      disclaimer in the documentation and/or other materials
14 4f917833 Sofia Papagiannaki
#      provided with the distribution.
15 2715ade4 Sofia Papagiannaki
#
16 4f917833 Sofia Papagiannaki
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 4f917833 Sofia Papagiannaki
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 4f917833 Sofia Papagiannaki
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 4f917833 Sofia Papagiannaki
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 4f917833 Sofia Papagiannaki
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 4f917833 Sofia Papagiannaki
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 4f917833 Sofia Papagiannaki
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 4f917833 Sofia Papagiannaki
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 4f917833 Sofia Papagiannaki
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 4f917833 Sofia Papagiannaki
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 4f917833 Sofia Papagiannaki
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 4f917833 Sofia Papagiannaki
# POSSIBILITY OF SUCH DAMAGE.
28 2715ade4 Sofia Papagiannaki
#
29 4f917833 Sofia Papagiannaki
# The views and conclusions contained in the software and
30 4f917833 Sofia Papagiannaki
# documentation are those of the authors and should not be
31 4f917833 Sofia Papagiannaki
# interpreted as representing official policies, either expressed
32 4f917833 Sofia Papagiannaki
# or implied, of GRNET S.A.
33 4f917833 Sofia Papagiannaki
34 4f917833 Sofia Papagiannaki
from time import time
35 5576e6dd Sofia Papagiannaki
from operator import itemgetter
36 5576e6dd Sofia Papagiannaki
from itertools import groupby
37 5576e6dd Sofia Papagiannaki
38 4100e0ee Sofia Papagiannaki
from sqlalchemy import (Table, Integer, BigInteger, DECIMAL, Boolean,
39 4100e0ee Sofia Papagiannaki
                        Column, String, MetaData, ForeignKey)
40 29148653 Sofia Papagiannaki
from sqlalchemy.schema import Index
41 29148653 Sofia Papagiannaki
from sqlalchemy.sql import func, and_, or_, not_, select, bindparam, exists
42 6a82f89f Sofia Papagiannaki
from sqlalchemy.exc import NoSuchTableError
43 4f1bc0a6 Sofia Papagiannaki
44 235a4227 Sofia Papagiannaki
from dbworker import DBWorker, ESCAPE_CHAR
45 4f917833 Sofia Papagiannaki
46 6e147ecc Antony Chazapis
from pithos.backends.filter import parse_filters
47 9d41b050 chazapis
48 059857e2 Antony Chazapis
49 2715ade4 Sofia Papagiannaki
ROOTNODE = 0
50 4f917833 Sofia Papagiannaki
51 2715ade4 Sofia Papagiannaki
(SERIAL, NODE, HASH, SIZE, TYPE, SOURCE, MTIME, MUSER, UUID, CHECKSUM,
52 876d7486 Sofia Papagiannaki
 CLUSTER, AVAILABLE, MAP_CHECK_TIMESTAMP) = range(13)
53 cf341da4 Antony Chazapis
54 2715ade4 Sofia Papagiannaki
(MATCH_PREFIX, MATCH_EXACT) = range(2)
55 4f917833 Sofia Papagiannaki
56 4f917833 Sofia Papagiannaki
inf = float('inf')
57 4f917833 Sofia Papagiannaki
58 4f917833 Sofia Papagiannaki
59 4f917833 Sofia Papagiannaki
def strnextling(prefix):
60 4f917833 Sofia Papagiannaki
    """Return the first unicode string
61 4f917833 Sofia Papagiannaki
       greater than but not starting with given prefix.
62 4f917833 Sofia Papagiannaki
       strnextling('hello') -> 'hellp'
63 4f917833 Sofia Papagiannaki
    """
64 4f917833 Sofia Papagiannaki
    if not prefix:
65 4f917833 Sofia Papagiannaki
        ## all strings start with the null string,
66 4f917833 Sofia Papagiannaki
        ## therefore we have to approximate strnextling('')
67 4f917833 Sofia Papagiannaki
        ## with the last unicode character supported by python
68 4f917833 Sofia Papagiannaki
        ## 0x10ffff for wide (32-bit unicode) python builds
69 4f917833 Sofia Papagiannaki
        ## 0x00ffff for narrow (16-bit unicode) python builds
70 4f917833 Sofia Papagiannaki
        ## We will not autodetect. 0xffff is safe enough.
71 4f917833 Sofia Papagiannaki
        return unichr(0xffff)
72 4f917833 Sofia Papagiannaki
    s = prefix[:-1]
73 4f917833 Sofia Papagiannaki
    c = ord(prefix[-1])
74 4f917833 Sofia Papagiannaki
    if c >= 0xffff:
75 4f917833 Sofia Papagiannaki
        raise RuntimeError
76 2715ade4 Sofia Papagiannaki
    s += unichr(c + 1)
77 4f917833 Sofia Papagiannaki
    return s
78 4f917833 Sofia Papagiannaki
79 2715ade4 Sofia Papagiannaki
80 4f917833 Sofia Papagiannaki
def strprevling(prefix):
81 4f917833 Sofia Papagiannaki
    """Return an approximation of the last unicode string
82 4f917833 Sofia Papagiannaki
       less than but not starting with given prefix.
83 4f917833 Sofia Papagiannaki
       strprevling(u'hello') -> u'helln\\xffff'
84 4f917833 Sofia Papagiannaki
    """
85 4f917833 Sofia Papagiannaki
    if not prefix:
86 4f917833 Sofia Papagiannaki
        ## There is no prevling for the null string
87 4f917833 Sofia Papagiannaki
        return prefix
88 4f917833 Sofia Papagiannaki
    s = prefix[:-1]
89 4f917833 Sofia Papagiannaki
    c = ord(prefix[-1])
90 4f917833 Sofia Papagiannaki
    if c > 0:
91 2715ade4 Sofia Papagiannaki
        s += unichr(c - 1) + unichr(0xffff)
92 4f917833 Sofia Papagiannaki
    return s
93 4f917833 Sofia Papagiannaki
94 4f917833 Sofia Papagiannaki
_propnames = {
95 2715ade4 Sofia Papagiannaki
    'serial': 0,
96 2715ade4 Sofia Papagiannaki
    'node': 1,
97 2715ade4 Sofia Papagiannaki
    'hash': 2,
98 2715ade4 Sofia Papagiannaki
    'size': 3,
99 2715ade4 Sofia Papagiannaki
    'type': 4,
100 2715ade4 Sofia Papagiannaki
    'source': 5,
101 2715ade4 Sofia Papagiannaki
    'mtime': 6,
102 2715ade4 Sofia Papagiannaki
    'muser': 7,
103 2715ade4 Sofia Papagiannaki
    'uuid': 8,
104 2715ade4 Sofia Papagiannaki
    'checksum': 9,
105 7fef13f0 Nanakos Chrysostomos
    'cluster': 10,
106 876d7486 Sofia Papagiannaki
    'available':11,
107 876d7486 Sofia Papagiannaki
    'map_check_timestamp':12
108 4f917833 Sofia Papagiannaki
}
109 4f917833 Sofia Papagiannaki
110 2715ade4 Sofia Papagiannaki
111 6a82f89f Sofia Papagiannaki
def create_tables(engine):
112 6a82f89f Sofia Papagiannaki
    metadata = MetaData()
113 2715ade4 Sofia Papagiannaki
114 6a82f89f Sofia Papagiannaki
    #create nodes table
115 2715ade4 Sofia Papagiannaki
    columns = []
116 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer, primary_key=True))
117 6a82f89f Sofia Papagiannaki
    columns.append(Column('parent', Integer,
118 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
119 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
120 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
121 6a82f89f Sofia Papagiannaki
                          autoincrement=False))
122 6a82f89f Sofia Papagiannaki
    columns.append(Column('latest_version', Integer))
123 6a82f89f Sofia Papagiannaki
    columns.append(Column('path', String(2048), default='', nullable=False))
124 6a82f89f Sofia Papagiannaki
    nodes = Table('nodes', metadata, *columns, mysql_engine='InnoDB')
125 6a82f89f Sofia Papagiannaki
    Index('idx_nodes_path', nodes.c.path, unique=True)
126 6a82f89f Sofia Papagiannaki
    Index('idx_nodes_parent', nodes.c.parent)
127 2715ade4 Sofia Papagiannaki
128 6a82f89f Sofia Papagiannaki
    #create policy table
129 2715ade4 Sofia Papagiannaki
    columns = []
130 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
131 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
132 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
133 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
134 6a82f89f Sofia Papagiannaki
                          primary_key=True))
135 6a82f89f Sofia Papagiannaki
    columns.append(Column('key', String(128), primary_key=True))
136 6a82f89f Sofia Papagiannaki
    columns.append(Column('value', String(256)))
137 29148653 Sofia Papagiannaki
    Table('policy', metadata, *columns, mysql_engine='InnoDB')
138 2715ade4 Sofia Papagiannaki
139 6a82f89f Sofia Papagiannaki
    #create statistics table
140 2715ade4 Sofia Papagiannaki
    columns = []
141 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
142 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
143 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
144 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
145 6a82f89f Sofia Papagiannaki
                          primary_key=True))
146 6a82f89f Sofia Papagiannaki
    columns.append(Column('population', Integer, nullable=False, default=0))
147 6a82f89f Sofia Papagiannaki
    columns.append(Column('size', BigInteger, nullable=False, default=0))
148 6a82f89f Sofia Papagiannaki
    columns.append(Column('mtime', DECIMAL(precision=16, scale=6)))
149 6a82f89f Sofia Papagiannaki
    columns.append(Column('cluster', Integer, nullable=False, default=0,
150 6a82f89f Sofia Papagiannaki
                          primary_key=True, autoincrement=False))
151 29148653 Sofia Papagiannaki
    Table('statistics', metadata, *columns, mysql_engine='InnoDB')
152 2715ade4 Sofia Papagiannaki
153 6a82f89f Sofia Papagiannaki
    #create versions table
154 2715ade4 Sofia Papagiannaki
    columns = []
155 6a82f89f Sofia Papagiannaki
    columns.append(Column('serial', Integer, primary_key=True))
156 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
157 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
158 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
159 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE')))
160 6a82f89f Sofia Papagiannaki
    columns.append(Column('hash', String(256)))
161 6a82f89f Sofia Papagiannaki
    columns.append(Column('size', BigInteger, nullable=False, default=0))
162 6a82f89f Sofia Papagiannaki
    columns.append(Column('type', String(256), nullable=False, default=''))
163 6a82f89f Sofia Papagiannaki
    columns.append(Column('source', Integer))
164 6a82f89f Sofia Papagiannaki
    columns.append(Column('mtime', DECIMAL(precision=16, scale=6)))
165 6a82f89f Sofia Papagiannaki
    columns.append(Column('muser', String(256), nullable=False, default=''))
166 6a82f89f Sofia Papagiannaki
    columns.append(Column('uuid', String(64), nullable=False, default=''))
167 6a82f89f Sofia Papagiannaki
    columns.append(Column('checksum', String(256), nullable=False, default=''))
168 6a82f89f Sofia Papagiannaki
    columns.append(Column('cluster', Integer, nullable=False, default=0))
169 876d7486 Sofia Papagiannaki
    columns.append(Column('available', Boolean, nullable=False, default=True))
170 876d7486 Sofia Papagiannaki
    columns.append(Column('map_check_timestamp', DECIMAL(precision=16,
171 876d7486 Sofia Papagiannaki
                                                         scale=6)))
172 6a82f89f Sofia Papagiannaki
    versions = Table('versions', metadata, *columns, mysql_engine='InnoDB')
173 6a82f89f Sofia Papagiannaki
    Index('idx_versions_node_mtime', versions.c.node, versions.c.mtime)
174 6a82f89f Sofia Papagiannaki
    Index('idx_versions_node_uuid', versions.c.uuid)
175 2715ade4 Sofia Papagiannaki
176 6a82f89f Sofia Papagiannaki
    #create attributes table
177 6a82f89f Sofia Papagiannaki
    columns = []
178 6a82f89f Sofia Papagiannaki
    columns.append(Column('serial', Integer,
179 6a82f89f Sofia Papagiannaki
                          ForeignKey('versions.serial',
180 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
181 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
182 6a82f89f Sofia Papagiannaki
                          primary_key=True))
183 6a82f89f Sofia Papagiannaki
    columns.append(Column('domain', String(256), primary_key=True))
184 6a82f89f Sofia Papagiannaki
    columns.append(Column('key', String(128), primary_key=True))
185 6a82f89f Sofia Papagiannaki
    columns.append(Column('value', String(256)))
186 4100e0ee Sofia Papagiannaki
    columns.append(Column('node', Integer, nullable=False, default=0))
187 4100e0ee Sofia Papagiannaki
    columns.append(Column('is_latest', Boolean, nullable=False, default=True))
188 6a82f89f Sofia Papagiannaki
    attributes = Table('attributes', metadata, *columns, mysql_engine='InnoDB')
189 5576e6dd Sofia Papagiannaki
    Index('idx_attributes_domain', attributes.c.domain)
190 4100e0ee Sofia Papagiannaki
    Index('idx_attributes_serial_node', attributes.c.serial, attributes.c.node)
191 2715ade4 Sofia Papagiannaki
192 6a82f89f Sofia Papagiannaki
    metadata.create_all(engine)
193 6a82f89f Sofia Papagiannaki
    return metadata.sorted_tables
194 4f917833 Sofia Papagiannaki
195 2715ade4 Sofia Papagiannaki
196 4f917833 Sofia Papagiannaki
class Node(DBWorker):
197 4f917833 Sofia Papagiannaki
    """Nodes store path organization and have multiple versions.
198 4f917833 Sofia Papagiannaki
       Versions store object history and have multiple attributes.
199 4f917833 Sofia Papagiannaki
       Attributes store metadata.
200 4f917833 Sofia Papagiannaki
    """
201 2715ade4 Sofia Papagiannaki
202 4f917833 Sofia Papagiannaki
    # TODO: Provide an interface for included and excluded clusters.
203 2715ade4 Sofia Papagiannaki
204 4f917833 Sofia Papagiannaki
    def __init__(self, **params):
205 4f917833 Sofia Papagiannaki
        DBWorker.__init__(self, **params)
206 6a82f89f Sofia Papagiannaki
        try:
207 6a82f89f Sofia Papagiannaki
            metadata = MetaData(self.engine)
208 6a82f89f Sofia Papagiannaki
            self.nodes = Table('nodes', metadata, autoload=True)
209 6a82f89f Sofia Papagiannaki
            self.policy = Table('policy', metadata, autoload=True)
210 6a82f89f Sofia Papagiannaki
            self.statistics = Table('statistics', metadata, autoload=True)
211 6a82f89f Sofia Papagiannaki
            self.versions = Table('versions', metadata, autoload=True)
212 6a82f89f Sofia Papagiannaki
            self.attributes = Table('attributes', metadata, autoload=True)
213 6a82f89f Sofia Papagiannaki
        except NoSuchTableError:
214 6a82f89f Sofia Papagiannaki
            tables = create_tables(self.engine)
215 6a82f89f Sofia Papagiannaki
            map(lambda t: self.__setattr__(t.name, t), tables)
216 2715ade4 Sofia Papagiannaki
217 d3be972a Sofia Papagiannaki
        s = self.nodes.select().where(and_(self.nodes.c.node == ROOTNODE,
218 d3be972a Sofia Papagiannaki
                                           self.nodes.c.parent == ROOTNODE))
219 26837206 Georgios D. Tsoukalas
        wrapper = self.wrapper
220 26837206 Georgios D. Tsoukalas
        wrapper.execute()
221 26837206 Georgios D. Tsoukalas
        try:
222 26837206 Georgios D. Tsoukalas
            rp = self.conn.execute(s)
223 26837206 Georgios D. Tsoukalas
            r = rp.fetchone()
224 26837206 Georgios D. Tsoukalas
            rp.close()
225 26837206 Georgios D. Tsoukalas
            if not r:
226 26837206 Georgios D. Tsoukalas
                s = self.nodes.insert(
227 26837206 Georgios D. Tsoukalas
                ).values(node=ROOTNODE, parent=ROOTNODE, path='')
228 26837206 Georgios D. Tsoukalas
                self.conn.execute(s)
229 26837206 Georgios D. Tsoukalas
        finally:
230 26837206 Georgios D. Tsoukalas
            wrapper.commit()
231 2715ade4 Sofia Papagiannaki
232 4f917833 Sofia Papagiannaki
    def node_create(self, parent, path):
233 4f917833 Sofia Papagiannaki
        """Create a new node from the given properties.
234 4f917833 Sofia Papagiannaki
           Return the node identifier of the new node.
235 4f917833 Sofia Papagiannaki
        """
236 18266c93 Sofia Papagiannaki
        #TODO catch IntegrityError?
237 18266c93 Sofia Papagiannaki
        s = self.nodes.insert().values(parent=parent, path=path)
238 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
239 18266c93 Sofia Papagiannaki
        inserted_primary_key = r.inserted_primary_key[0]
240 18266c93 Sofia Papagiannaki
        r.close()
241 18266c93 Sofia Papagiannaki
        return inserted_primary_key
242 2715ade4 Sofia Papagiannaki
243 985b9b09 Sofia Papagiannaki
    def node_lookup(self, path, for_update=False):
244 4f917833 Sofia Papagiannaki
        """Lookup the current node of the given path.
245 4f917833 Sofia Papagiannaki
           Return None if the path is not found.
246 4f917833 Sofia Papagiannaki
        """
247 2715ade4 Sofia Papagiannaki
248 6b20cfbc Antony Chazapis
        # Use LIKE for comparison to avoid MySQL problems with trailing spaces.
249 96090728 Sofia Papagiannaki
        s = select([self.nodes.c.node],
250 96090728 Sofia Papagiannaki
                   self.nodes.c.path.like(self.escape_like(path),
251 96090728 Sofia Papagiannaki
                                          escape=ESCAPE_CHAR),
252 96090728 Sofia Papagiannaki
                   for_update=for_update)
253 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
254 18266c93 Sofia Papagiannaki
        row = r.fetchone()
255 18266c93 Sofia Papagiannaki
        r.close()
256 18266c93 Sofia Papagiannaki
        if row:
257 18266c93 Sofia Papagiannaki
            return row[0]
258 4f917833 Sofia Papagiannaki
        return None
259 2715ade4 Sofia Papagiannaki
260 8221c89d Sofia Papagiannaki
    def node_lookup_bulk(self, paths):
261 8221c89d Sofia Papagiannaki
        """Lookup the current nodes for the given paths.
262 8221c89d Sofia Papagiannaki
           Return () if the path is not found.
263 8221c89d Sofia Papagiannaki
        """
264 2715ade4 Sofia Papagiannaki
265 c53502b1 Sofia Papagiannaki
        if not paths:
266 c53502b1 Sofia Papagiannaki
            return ()
267 8221c89d Sofia Papagiannaki
        # Use LIKE for comparison to avoid MySQL problems with trailing spaces.
268 8221c89d Sofia Papagiannaki
        s = select([self.nodes.c.node], self.nodes.c.path.in_(paths))
269 8221c89d Sofia Papagiannaki
        r = self.conn.execute(s)
270 8221c89d Sofia Papagiannaki
        rows = r.fetchall()
271 8221c89d Sofia Papagiannaki
        r.close()
272 8221c89d Sofia Papagiannaki
        return [row[0] for row in rows]
273 2715ade4 Sofia Papagiannaki
274 4f917833 Sofia Papagiannaki
    def node_get_properties(self, node):
275 4f917833 Sofia Papagiannaki
        """Return the node's (parent, path).
276 4f917833 Sofia Papagiannaki
           Return None if the node is not found.
277 4f917833 Sofia Papagiannaki
        """
278 2715ade4 Sofia Papagiannaki
279 18266c93 Sofia Papagiannaki
        s = select([self.nodes.c.parent, self.nodes.c.path])
280 18266c93 Sofia Papagiannaki
        s = s.where(self.nodes.c.node == node)
281 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
282 18266c93 Sofia Papagiannaki
        l = r.fetchone()
283 18266c93 Sofia Papagiannaki
        r.close()
284 18266c93 Sofia Papagiannaki
        return l
285 2715ade4 Sofia Papagiannaki
286 4f917833 Sofia Papagiannaki
    def node_get_versions(self, node, keys=(), propnames=_propnames):
287 4f917833 Sofia Papagiannaki
        """Return the properties of all versions at node.
288 4f917833 Sofia Papagiannaki
           If keys is empty, return all properties in the order
289 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
290 876d7486 Sofia Papagiannaki
            checksum, cluster, available, map_check_timestamp).
291 4f917833 Sofia Papagiannaki
        """
292 2715ade4 Sofia Papagiannaki
293 1c2fc0ff Antony Chazapis
        s = select([self.versions.c.serial,
294 1c2fc0ff Antony Chazapis
                    self.versions.c.node,
295 1c2fc0ff Antony Chazapis
                    self.versions.c.hash,
296 1c2fc0ff Antony Chazapis
                    self.versions.c.size,
297 cf341da4 Antony Chazapis
                    self.versions.c.type,
298 1c2fc0ff Antony Chazapis
                    self.versions.c.source,
299 1c2fc0ff Antony Chazapis
                    self.versions.c.mtime,
300 1c2fc0ff Antony Chazapis
                    self.versions.c.muser,
301 25ae8b75 Antony Chazapis
                    self.versions.c.uuid,
302 f7d5b0cf Antony Chazapis
                    self.versions.c.checksum,
303 876d7486 Sofia Papagiannaki
                    self.versions.c.cluster,
304 876d7486 Sofia Papagiannaki
                    self.versions.c.available,
305 876d7486 Sofia Papagiannaki
                    self.versions.c.map_check_timestamp],
306 876d7486 Sofia Papagiannaki
                   self.versions.c.node == node)
307 cc28f894 Sofia Papagiannaki
        s = s.order_by(self.versions.c.serial)
308 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
309 18266c93 Sofia Papagiannaki
        rows = r.fetchall()
310 9eb713e1 Sofia Papagiannaki
        r.close()
311 18266c93 Sofia Papagiannaki
        if not rows:
312 18266c93 Sofia Papagiannaki
            return rows
313 2715ade4 Sofia Papagiannaki
314 4f917833 Sofia Papagiannaki
        if not keys:
315 18266c93 Sofia Papagiannaki
            return rows
316 2715ade4 Sofia Papagiannaki
317 29148653 Sofia Papagiannaki
        return [[p[propnames[k]] for k in keys if k in propnames] for
318 29148653 Sofia Papagiannaki
                p in rows]
319 2715ade4 Sofia Papagiannaki
320 4f917833 Sofia Papagiannaki
    def node_count_children(self, node):
321 4f917833 Sofia Papagiannaki
        """Return node's child count."""
322 2715ade4 Sofia Papagiannaki
323 18266c93 Sofia Papagiannaki
        s = select([func.count(self.nodes.c.node)])
324 18266c93 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.parent == node,
325 18266c93 Sofia Papagiannaki
                         self.nodes.c.node != ROOTNODE))
326 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
327 18266c93 Sofia Papagiannaki
        row = r.fetchone()
328 18266c93 Sofia Papagiannaki
        r.close()
329 18266c93 Sofia Papagiannaki
        return row[0]
330 2715ade4 Sofia Papagiannaki
331 0f510652 Sofia Papagiannaki
    def node_purge_children(self, parent, before=inf, cluster=0,
332 0f510652 Sofia Papagiannaki
                            update_statistics_ancestors_depth=None):
333 4f917833 Sofia Papagiannaki
        """Delete all versions with the specified
334 4f917833 Sofia Papagiannaki
           parent and cluster, and return
335 0a92ff85 Sofia Papagiannaki
           the hashes, the total size and the serials of versions deleted.
336 4f917833 Sofia Papagiannaki
           Clears out nodes with no remaining versions.
337 4f917833 Sofia Papagiannaki
        """
338 b43d44ad Sofia Papagiannaki
        #update statistics
339 b43d44ad Sofia Papagiannaki
        c1 = select([self.nodes.c.node],
340 2715ade4 Sofia Papagiannaki
                    self.nodes.c.parent == parent)
341 b43d44ad Sofia Papagiannaki
        where_clause = and_(self.versions.c.node.in_(c1),
342 62d938dc Sofia Papagiannaki
                            self.versions.c.cluster == cluster)
343 0a92ff85 Sofia Papagiannaki
        if before != inf:
344 0a92ff85 Sofia Papagiannaki
            where_clause = and_(where_clause,
345 0a92ff85 Sofia Papagiannaki
                                self.versions.c.mtime <= before)
346 18266c93 Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
347 18266c93 Sofia Papagiannaki
                    func.sum(self.versions.c.size)])
348 18266c93 Sofia Papagiannaki
        s = s.where(where_clause)
349 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
350 18266c93 Sofia Papagiannaki
        row = r.fetchone()
351 18266c93 Sofia Papagiannaki
        r.close()
352 18266c93 Sofia Papagiannaki
        if not row:
353 442bc80c Sofia Papagiannaki
            return (), 0, ()
354 0a92ff85 Sofia Papagiannaki
        nr, size = row[0], row[1] if row[1] else 0
355 4f917833 Sofia Papagiannaki
        mtime = time()
356 0a92ff85 Sofia Papagiannaki
        self.statistics_update(parent, -nr, -size, mtime, cluster)
357 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(parent, -nr, -size, mtime, cluster,
358 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
359 2715ade4 Sofia Papagiannaki
360 388ea25f Sofia Papagiannaki
        s = select([self.versions.c.hash, self.versions.c.serial])
361 18266c93 Sofia Papagiannaki
        s = s.where(where_clause)
362 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
363 388ea25f Sofia Papagiannaki
        hashes = []
364 388ea25f Sofia Papagiannaki
        serials = []
365 388ea25f Sofia Papagiannaki
        for row in r.fetchall():
366 388ea25f Sofia Papagiannaki
            hashes += [row[0]]
367 388ea25f Sofia Papagiannaki
            serials += [row[1]]
368 18266c93 Sofia Papagiannaki
        r.close()
369 2715ade4 Sofia Papagiannaki
370 b43d44ad Sofia Papagiannaki
        #delete versions
371 18266c93 Sofia Papagiannaki
        s = self.versions.delete().where(where_clause)
372 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
373 18266c93 Sofia Papagiannaki
        r.close()
374 2715ade4 Sofia Papagiannaki
375 18266c93 Sofia Papagiannaki
        #delete nodes
376 b43d44ad Sofia Papagiannaki
        s = select([self.nodes.c.node],
377 2715ade4 Sofia Papagiannaki
                   and_(self.nodes.c.parent == parent,
378 2715ade4 Sofia Papagiannaki
                        select([func.count(self.versions.c.serial)],
379 29148653 Sofia Papagiannaki
                               self.versions.c.node == self.nodes.c.node).
380 29148653 Sofia Papagiannaki
                        as_scalar() == 0))
381 70516d86 Sofia Papagiannaki
        rp = self.conn.execute(s)
382 29148653 Sofia Papagiannaki
        nodes = [row[0] for row in rp.fetchall()]
383 70516d86 Sofia Papagiannaki
        rp.close()
384 c53502b1 Sofia Papagiannaki
        if nodes:
385 c53502b1 Sofia Papagiannaki
            s = self.nodes.delete().where(self.nodes.c.node.in_(nodes))
386 c53502b1 Sofia Papagiannaki
            self.conn.execute(s).close()
387 2715ade4 Sofia Papagiannaki
388 388ea25f Sofia Papagiannaki
        return hashes, size, serials
389 2715ade4 Sofia Papagiannaki
390 0f510652 Sofia Papagiannaki
    def node_purge(self, node, before=inf, cluster=0,
391 0f510652 Sofia Papagiannaki
                   update_statistics_ancestors_depth=None):
392 4f917833 Sofia Papagiannaki
        """Delete all versions with the specified
393 4f917833 Sofia Papagiannaki
           node and cluster, and return
394 813e42e5 Antony Chazapis
           the hashes and size of versions deleted.
395 4f917833 Sofia Papagiannaki
           Clears out the node if it has no remaining versions.
396 4f917833 Sofia Papagiannaki
        """
397 2715ade4 Sofia Papagiannaki
398 b43d44ad Sofia Papagiannaki
        #update statistics
399 b43d44ad Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
400 b43d44ad Sofia Papagiannaki
                    func.sum(self.versions.c.size)])
401 b43d44ad Sofia Papagiannaki
        where_clause = and_(self.versions.c.node == node,
402 2715ade4 Sofia Papagiannaki
                            self.versions.c.cluster == cluster)
403 62d938dc Sofia Papagiannaki
        if before != inf:
404 0a92ff85 Sofia Papagiannaki
            where_clause = and_(where_clause,
405 0a92ff85 Sofia Papagiannaki
                                self.versions.c.mtime <= before)
406 0a92ff85 Sofia Papagiannaki
        s = s.where(where_clause)
407 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
408 b43d44ad Sofia Papagiannaki
        row = r.fetchone()
409 b43d44ad Sofia Papagiannaki
        nr, size = row[0], row[1]
410 b43d44ad Sofia Papagiannaki
        r.close()
411 4f917833 Sofia Papagiannaki
        if not nr:
412 442bc80c Sofia Papagiannaki
            return (), 0, ()
413 4f917833 Sofia Papagiannaki
        mtime = time()
414 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -nr, -size, mtime, cluster,
415 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
416 2715ade4 Sofia Papagiannaki
417 388ea25f Sofia Papagiannaki
        s = select([self.versions.c.hash, self.versions.c.serial])
418 b43d44ad Sofia Papagiannaki
        s = s.where(where_clause)
419 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
420 388ea25f Sofia Papagiannaki
        hashes = []
421 388ea25f Sofia Papagiannaki
        serials = []
422 388ea25f Sofia Papagiannaki
        for row in r.fetchall():
423 388ea25f Sofia Papagiannaki
            hashes += [row[0]]
424 388ea25f Sofia Papagiannaki
            serials += [row[1]]
425 9eb713e1 Sofia Papagiannaki
        r.close()
426 2715ade4 Sofia Papagiannaki
427 b43d44ad Sofia Papagiannaki
        #delete versions
428 b43d44ad Sofia Papagiannaki
        s = self.versions.delete().where(where_clause)
429 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
430 b43d44ad Sofia Papagiannaki
        r.close()
431 2715ade4 Sofia Papagiannaki
432 b43d44ad Sofia Papagiannaki
        #delete nodes
433 b43d44ad Sofia Papagiannaki
        s = select([self.nodes.c.node],
434 2715ade4 Sofia Papagiannaki
                   and_(self.nodes.c.node == node,
435 2715ade4 Sofia Papagiannaki
                        select([func.count(self.versions.c.serial)],
436 29148653 Sofia Papagiannaki
                               self.versions.c.node == self.nodes.c.node).
437 29148653 Sofia Papagiannaki
                        as_scalar() == 0))
438 29148653 Sofia Papagiannaki
        rp = self.conn.execute(s)
439 29148653 Sofia Papagiannaki
        nodes = [row[0] for row in rp.fetchall()]
440 0a92ff85 Sofia Papagiannaki
        rp.close()
441 c53502b1 Sofia Papagiannaki
        if nodes:
442 c53502b1 Sofia Papagiannaki
            s = self.nodes.delete().where(self.nodes.c.node.in_(nodes))
443 c53502b1 Sofia Papagiannaki
            self.conn.execute(s).close()
444 2715ade4 Sofia Papagiannaki
445 388ea25f Sofia Papagiannaki
        return hashes, size, serials
446 2715ade4 Sofia Papagiannaki
447 0f510652 Sofia Papagiannaki
    def node_remove(self, node, update_statistics_ancestors_depth=None):
448 4f917833 Sofia Papagiannaki
        """Remove the node specified.
449 4f917833 Sofia Papagiannaki
           Return false if the node has children or is not found.
450 4f917833 Sofia Papagiannaki
        """
451 2715ade4 Sofia Papagiannaki
452 4f917833 Sofia Papagiannaki
        if self.node_count_children(node):
453 4f917833 Sofia Papagiannaki
            return False
454 2715ade4 Sofia Papagiannaki
455 4f917833 Sofia Papagiannaki
        mtime = time()
456 b43d44ad Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
457 b43d44ad Sofia Papagiannaki
                    func.sum(self.versions.c.size),
458 b43d44ad Sofia Papagiannaki
                    self.versions.c.cluster])
459 b43d44ad Sofia Papagiannaki
        s = s.where(self.versions.c.node == node)
460 b43d44ad Sofia Papagiannaki
        s = s.group_by(self.versions.c.cluster)
461 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
462 b43d44ad Sofia Papagiannaki
        for population, size, cluster in r.fetchall():
463 2715ade4 Sofia Papagiannaki
            self.statistics_update_ancestors(
464 0f510652 Sofia Papagiannaki
                node, -population, -size, mtime, cluster,
465 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth)
466 b43d44ad Sofia Papagiannaki
        r.close()
467 2715ade4 Sofia Papagiannaki
468 b43d44ad Sofia Papagiannaki
        s = self.nodes.delete().where(self.nodes.c.node == node)
469 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
470 4f917833 Sofia Papagiannaki
        return True
471 2715ade4 Sofia Papagiannaki
472 d1e7d2b4 Sofia Papagiannaki
    def node_accounts(self, accounts=()):
473 d1e7d2b4 Sofia Papagiannaki
        s = select([self.nodes.c.path, self.nodes.c.node])
474 d1e7d2b4 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.node != 0,
475 d1e7d2b4 Sofia Papagiannaki
                         self.nodes.c.parent == 0))
476 d1e7d2b4 Sofia Papagiannaki
        if accounts:
477 d1e7d2b4 Sofia Papagiannaki
            s = s.where(self.nodes.c.path.in_(accounts))
478 d1e7d2b4 Sofia Papagiannaki
        r = self.conn.execute(s)
479 d1e7d2b4 Sofia Papagiannaki
        rows = r.fetchall()
480 d1e7d2b4 Sofia Papagiannaki
        r.close()
481 d1e7d2b4 Sofia Papagiannaki
        return rows
482 d1e7d2b4 Sofia Papagiannaki
483 ae6199c5 Sofia Papagiannaki
    def node_account_quotas(self):
484 ae6199c5 Sofia Papagiannaki
        s = select([self.nodes.c.path, self.policy.c.value])
485 ae6199c5 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.node != 0,
486 ae6199c5 Sofia Papagiannaki
                         self.nodes.c.parent == 0))
487 ae6199c5 Sofia Papagiannaki
        s = s.where(self.nodes.c.node == self.policy.c.node)
488 ae6199c5 Sofia Papagiannaki
        s = s.where(self.policy.c.key == 'quota')
489 ae6199c5 Sofia Papagiannaki
        r = self.conn.execute(s)
490 ae6199c5 Sofia Papagiannaki
        rows = r.fetchall()
491 ae6199c5 Sofia Papagiannaki
        r.close()
492 ae6199c5 Sofia Papagiannaki
        return dict(rows)
493 ae6199c5 Sofia Papagiannaki
494 2ff02341 Sofia Papagiannaki
    def node_account_usage(self, account=None, cluster=0):
495 2ff02341 Sofia Papagiannaki
        """Return usage for a specific account.
496 2ff02341 Sofia Papagiannaki

497 2ff02341 Sofia Papagiannaki
        Keyword arguments:
498 2ff02341 Sofia Papagiannaki
        account -- (default None: list usage for all the accounts)
499 2ff02341 Sofia Papagiannaki
        cluster -- list current, history or deleted usage (default 0: normal)
500 2ff02341 Sofia Papagiannaki
        """
501 2ff02341 Sofia Papagiannaki
502 2ff02341 Sofia Papagiannaki
        n1 = self.nodes.alias('n1')
503 2ff02341 Sofia Papagiannaki
        n2 = self.nodes.alias('n2')
504 2ff02341 Sofia Papagiannaki
        n3 = self.nodes.alias('n3')
505 2ff02341 Sofia Papagiannaki
506 2ff02341 Sofia Papagiannaki
        s = select([n3.c.path, func.sum(self.versions.c.size)])
507 2ff02341 Sofia Papagiannaki
        s = s.where(n1.c.node == self.versions.c.node)
508 d1e7d2b4 Sofia Papagiannaki
        s = s.where(self.versions.c.cluster == cluster)
509 2ff02341 Sofia Papagiannaki
        s = s.where(n1.c.parent == n2.c.node)
510 2ff02341 Sofia Papagiannaki
        s = s.where(n2.c.parent == n3.c.node)
511 2ff02341 Sofia Papagiannaki
        s = s.where(n3.c.parent == 0)
512 2ff02341 Sofia Papagiannaki
        s = s.where(n3.c.node != 0)
513 2ff02341 Sofia Papagiannaki
        if account:
514 2ff02341 Sofia Papagiannaki
            s = s.where(n3.c.path == account)
515 2ff02341 Sofia Papagiannaki
        s = s.group_by(n3.c.path)
516 d1e7d2b4 Sofia Papagiannaki
        r = self.conn.execute(s)
517 2ff02341 Sofia Papagiannaki
        usage = r.fetchall()
518 d1e7d2b4 Sofia Papagiannaki
        r.close()
519 2ff02341 Sofia Papagiannaki
        return dict(usage)
520 94bff756 Sofia Papagiannaki
521 5e7485da Antony Chazapis
    def policy_get(self, node):
522 6a82f89f Sofia Papagiannaki
        s = select([self.policy.c.key, self.policy.c.value],
523 2715ade4 Sofia Papagiannaki
                   self.policy.c.node == node)
524 5e7485da Antony Chazapis
        r = self.conn.execute(s)
525 5e7485da Antony Chazapis
        d = dict(r.fetchall())
526 5e7485da Antony Chazapis
        r.close()
527 5e7485da Antony Chazapis
        return d
528 2715ade4 Sofia Papagiannaki
529 5e7485da Antony Chazapis
    def policy_set(self, node, policy):
530 5e7485da Antony Chazapis
        #insert or replace
531 5e7485da Antony Chazapis
        for k, v in policy.iteritems():
532 6a82f89f Sofia Papagiannaki
            s = self.policy.update().where(and_(self.policy.c.node == node,
533 2715ade4 Sofia Papagiannaki
                                                self.policy.c.key == k))
534 2715ade4 Sofia Papagiannaki
            s = s.values(value=v)
535 5e7485da Antony Chazapis
            rp = self.conn.execute(s)
536 5e7485da Antony Chazapis
            rp.close()
537 5e7485da Antony Chazapis
            if rp.rowcount == 0:
538 6a82f89f Sofia Papagiannaki
                s = self.policy.insert()
539 2715ade4 Sofia Papagiannaki
                values = {'node': node, 'key': k, 'value': v}
540 5e7485da Antony Chazapis
                r = self.conn.execute(s, values)
541 5e7485da Antony Chazapis
                r.close()
542 2715ade4 Sofia Papagiannaki
543 4f917833 Sofia Papagiannaki
    def statistics_get(self, node, cluster=0):
544 4f917833 Sofia Papagiannaki
        """Return population, total size and last mtime
545 4f917833 Sofia Papagiannaki
           for all versions under node that belong to the cluster.
546 4f917833 Sofia Papagiannaki
        """
547 2715ade4 Sofia Papagiannaki
548 b43d44ad Sofia Papagiannaki
        s = select([self.statistics.c.population,
549 b43d44ad Sofia Papagiannaki
                    self.statistics.c.size,
550 b43d44ad Sofia Papagiannaki
                    self.statistics.c.mtime])
551 b43d44ad Sofia Papagiannaki
        s = s.where(and_(self.statistics.c.node == node,
552 b43d44ad Sofia Papagiannaki
                         self.statistics.c.cluster == cluster))
553 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
554 b43d44ad Sofia Papagiannaki
        row = r.fetchone()
555 b43d44ad Sofia Papagiannaki
        r.close()
556 b43d44ad Sofia Papagiannaki
        return row
557 2715ade4 Sofia Papagiannaki
558 4f917833 Sofia Papagiannaki
    def statistics_update(self, node, population, size, mtime, cluster=0):
559 4f917833 Sofia Papagiannaki
        """Update the statistics of the given node.
560 4f917833 Sofia Papagiannaki
           Statistics keep track the population, total
561 4f917833 Sofia Papagiannaki
           size of objects and mtime in the node's namespace.
562 4f917833 Sofia Papagiannaki
           May be zero or positive or negative numbers.
563 4f917833 Sofia Papagiannaki
        """
564 18266c93 Sofia Papagiannaki
        s = select([self.statistics.c.population, self.statistics.c.size],
565 2715ade4 Sofia Papagiannaki
                   and_(self.statistics.c.node == node,
566 2715ade4 Sofia Papagiannaki
                        self.statistics.c.cluster == cluster))
567 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
568 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
569 b43d44ad Sofia Papagiannaki
        rp.close()
570 18266c93 Sofia Papagiannaki
        if not r:
571 4f917833 Sofia Papagiannaki
            prepopulation, presize = (0, 0)
572 4f917833 Sofia Papagiannaki
        else:
573 4f917833 Sofia Papagiannaki
            prepopulation, presize = r
574 4f917833 Sofia Papagiannaki
        population += prepopulation
575 096a7c3b Sofia Papagiannaki
        population = max(population, 0)
576 4f917833 Sofia Papagiannaki
        size += presize
577 2715ade4 Sofia Papagiannaki
578 d3be972a Sofia Papagiannaki
        #insert or replace
579 70516d86 Sofia Papagiannaki
        #TODO better upsert
580 29148653 Sofia Papagiannaki
        u = self.statistics.update().where(and_(
581 29148653 Sofia Papagiannaki
            self.statistics.c.node == node,
582 29148653 Sofia Papagiannaki
            self.statistics.c.cluster == cluster))
583 d3be972a Sofia Papagiannaki
        u = u.values(population=population, size=size, mtime=mtime)
584 d3be972a Sofia Papagiannaki
        rp = self.conn.execute(u)
585 d3be972a Sofia Papagiannaki
        rp.close()
586 d3be972a Sofia Papagiannaki
        if rp.rowcount == 0:
587 d3be972a Sofia Papagiannaki
            ins = self.statistics.insert()
588 d3be972a Sofia Papagiannaki
            ins = ins.values(node=node, population=population, size=size,
589 d3be972a Sofia Papagiannaki
                             mtime=mtime, cluster=cluster)
590 d3be972a Sofia Papagiannaki
            self.conn.execute(ins).close()
591 2715ade4 Sofia Papagiannaki
592 0f510652 Sofia Papagiannaki
    def statistics_update_ancestors(self, node, population, size, mtime,
593 0f510652 Sofia Papagiannaki
                                    cluster=0, recursion_depth=None):
594 4f917833 Sofia Papagiannaki
        """Update the statistics of the given node's parent.
595 0f510652 Sofia Papagiannaki
           Then recursively update all parents up to the root
596 0f510652 Sofia Papagiannaki
           or up to the ``recursion_depth`` (if not None).
597 4f917833 Sofia Papagiannaki
           Population is not recursive.
598 4f917833 Sofia Papagiannaki
        """
599 2715ade4 Sofia Papagiannaki
600 0f510652 Sofia Papagiannaki
        i = 0
601 4f917833 Sofia Papagiannaki
        while True:
602 18266c93 Sofia Papagiannaki
            if node == ROOTNODE:
603 4f917833 Sofia Papagiannaki
                break
604 2e8edd42 Sofia Papagiannaki
            if recursion_depth and recursion_depth <= i:
605 0f510652 Sofia Papagiannaki
                break
606 4f917833 Sofia Papagiannaki
            props = self.node_get_properties(node)
607 4f917833 Sofia Papagiannaki
            if props is None:
608 4f917833 Sofia Papagiannaki
                break
609 4f917833 Sofia Papagiannaki
            parent, path = props
610 4f917833 Sofia Papagiannaki
            self.statistics_update(parent, population, size, mtime, cluster)
611 4f917833 Sofia Papagiannaki
            node = parent
612 2715ade4 Sofia Papagiannaki
            population = 0  # Population isn't recursive
613 0f510652 Sofia Papagiannaki
            i += 1
614 2715ade4 Sofia Papagiannaki
615 4f917833 Sofia Papagiannaki
    def statistics_latest(self, node, before=inf, except_cluster=0):
616 4f917833 Sofia Papagiannaki
        """Return population, total size and last mtime
617 4f917833 Sofia Papagiannaki
           for all latest versions under node that
618 4f917833 Sofia Papagiannaki
           do not belong to the cluster.
619 4f917833 Sofia Papagiannaki
        """
620 2715ade4 Sofia Papagiannaki
621 4f917833 Sofia Papagiannaki
        # The node.
622 4f917833 Sofia Papagiannaki
        props = self.node_get_properties(node)
623 4f917833 Sofia Papagiannaki
        if props is None:
624 4f917833 Sofia Papagiannaki
            return None
625 4f917833 Sofia Papagiannaki
        parent, path = props
626 2715ade4 Sofia Papagiannaki
627 4f917833 Sofia Papagiannaki
        # The latest version.
628 b43d44ad Sofia Papagiannaki
        s = select([self.versions.c.serial,
629 b43d44ad Sofia Papagiannaki
                    self.versions.c.node,
630 1c2fc0ff Antony Chazapis
                    self.versions.c.hash,
631 b43d44ad Sofia Papagiannaki
                    self.versions.c.size,
632 cf341da4 Antony Chazapis
                    self.versions.c.type,
633 d0aacf54 Sofia Papagiannaki
                    self.versions.c.source,
634 b43d44ad Sofia Papagiannaki
                    self.versions.c.mtime,
635 b43d44ad Sofia Papagiannaki
                    self.versions.c.muser,
636 25ae8b75 Antony Chazapis
                    self.versions.c.uuid,
637 f7d5b0cf Antony Chazapis
                    self.versions.c.checksum,
638 6e3e5c84 Sofia Papagiannaki
                    self.versions.c.cluster,
639 6e3e5c84 Sofia Papagiannaki
                    self.versions.c.available,
640 6e3e5c84 Sofia Papagiannaki
                    self.versions.c.map_check_timestamp])
641 62d938dc Sofia Papagiannaki
        if before != inf:
642 585b75e7 Sofia Papagiannaki
            filtered = select([func.max(self.versions.c.serial)],
643 2715ade4 Sofia Papagiannaki
                              self.versions.c.node == node)
644 62d938dc Sofia Papagiannaki
            filtered = filtered.where(self.versions.c.mtime < before)
645 585b75e7 Sofia Papagiannaki
        else:
646 585b75e7 Sofia Papagiannaki
            filtered = select([self.nodes.c.latest_version],
647 2e8edd42 Sofia Papagiannaki
                              self.nodes.c.node == node)
648 b43d44ad Sofia Papagiannaki
        s = s.where(and_(self.versions.c.cluster != except_cluster,
649 62d938dc Sofia Papagiannaki
                         self.versions.c.serial == filtered))
650 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
651 b43d44ad Sofia Papagiannaki
        props = r.fetchone()
652 b43d44ad Sofia Papagiannaki
        r.close()
653 b43d44ad Sofia Papagiannaki
        if not props:
654 4f917833 Sofia Papagiannaki
            return None
655 4f917833 Sofia Papagiannaki
        mtime = props[MTIME]
656 2715ade4 Sofia Papagiannaki
657 4f917833 Sofia Papagiannaki
        # First level, just under node (get population).
658 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
659 b43d44ad Sofia Papagiannaki
        s = select([func.count(v.c.serial),
660 b43d44ad Sofia Papagiannaki
                    func.sum(v.c.size),
661 b43d44ad Sofia Papagiannaki
                    func.max(v.c.mtime)])
662 62d938dc Sofia Papagiannaki
        if before != inf:
663 87835e94 Nanakos Chrysostomos
            c1 = select([func.max(self.versions.c.serial)],
664 87835e94 Nanakos Chrysostomos
                        and_(self.versions.c.mtime < before,
665 87835e94 Nanakos Chrysostomos
                             self.versions.c.node == v.c.node))
666 585b75e7 Sofia Papagiannaki
        else:
667 585b75e7 Sofia Papagiannaki
            c1 = select([self.nodes.c.latest_version])
668 2e8edd42 Sofia Papagiannaki
            c1 = c1.where(self.nodes.c.node == v.c.node)
669 b43d44ad Sofia Papagiannaki
        c2 = select([self.nodes.c.node], self.nodes.c.parent == node)
670 585b75e7 Sofia Papagiannaki
        s = s.where(and_(v.c.serial == c1,
671 b43d44ad Sofia Papagiannaki
                         v.c.cluster != except_cluster,
672 b43d44ad Sofia Papagiannaki
                         v.c.node.in_(c2)))
673 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
674 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
675 b43d44ad Sofia Papagiannaki
        rp.close()
676 b43d44ad Sofia Papagiannaki
        if not r:
677 4f917833 Sofia Papagiannaki
            return None
678 4f917833 Sofia Papagiannaki
        count = r[0]
679 4f917833 Sofia Papagiannaki
        mtime = max(mtime, r[2])
680 4f917833 Sofia Papagiannaki
        if count == 0:
681 4f917833 Sofia Papagiannaki
            return (0, 0, mtime)
682 2715ade4 Sofia Papagiannaki
683 4f917833 Sofia Papagiannaki
        # All children (get size and mtime).
684 92da0e5a Antony Chazapis
        # This is why the full path is stored.
685 7fef13f0 Nanakos Chrysostomos
        if before != inf:
686 7fef13f0 Nanakos Chrysostomos
            s = select([func.count(v.c.serial),
687 cbe81673 Chrysostomos Nanakos
                       func.sum(v.c.size),
688 cbe81673 Chrysostomos Nanakos
                       func.max(v.c.mtime)])
689 585b75e7 Sofia Papagiannaki
            c1 = select([func.max(self.versions.c.serial)],
690 87835e94 Nanakos Chrysostomos
                        and_(self.versions.c.mtime < before,
691 87835e94 Nanakos Chrysostomos
                             self.versions.c.node == v.c.node))
692 585b75e7 Sofia Papagiannaki
        else:
693 7fef13f0 Nanakos Chrysostomos
            inner_join = \
694 cbe81673 Chrysostomos Nanakos
                self.versions.join(self.nodes, onclause=
695 cbe81673 Chrysostomos Nanakos
                                   self.versions.c.serial ==
696 cbe81673 Chrysostomos Nanakos
                                   self.nodes.c.latest_version)
697 7fef13f0 Nanakos Chrysostomos
            s = select([func.count(self.versions.c.serial),
698 cbe81673 Chrysostomos Nanakos
                       func.sum(self.versions.c.size),
699 cbe81673 Chrysostomos Nanakos
                       func.max(self.versions.c.mtime)],
700 cbe81673 Chrysostomos Nanakos
                       from_obj=[inner_join])
701 7fef13f0 Nanakos Chrysostomos
702 96090728 Sofia Papagiannaki
        c2 = select([self.nodes.c.node],
703 96090728 Sofia Papagiannaki
                    self.nodes.c.path.like(self.escape_like(path) + '%',
704 96090728 Sofia Papagiannaki
                                           escape=ESCAPE_CHAR))
705 7fef13f0 Nanakos Chrysostomos
        if before != inf:
706 7fef13f0 Nanakos Chrysostomos
            s = s.where(and_(v.c.serial == c1,
707 cbe81673 Chrysostomos Nanakos
                        v.c.cluster != except_cluster,
708 cbe81673 Chrysostomos Nanakos
                        v.c.node.in_(c2)))
709 7fef13f0 Nanakos Chrysostomos
        else:
710 7fef13f0 Nanakos Chrysostomos
            s = s.where(and_(self.versions.c.cluster != except_cluster,
711 7fef13f0 Nanakos Chrysostomos
                        self.versions.c.node.in_(c2)))
712 7fef13f0 Nanakos Chrysostomos
713 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
714 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
715 b43d44ad Sofia Papagiannaki
        rp.close()
716 b43d44ad Sofia Papagiannaki
        if not r:
717 4f917833 Sofia Papagiannaki
            return None
718 87835e94 Nanakos Chrysostomos
        size = long(r[1] - props[SIZE])
719 4f917833 Sofia Papagiannaki
        mtime = max(mtime, r[2])
720 4f917833 Sofia Papagiannaki
        return (count, size, mtime)
721 2715ade4 Sofia Papagiannaki
722 585b75e7 Sofia Papagiannaki
    def nodes_set_latest_version(self, node, serial):
723 585b75e7 Sofia Papagiannaki
        s = self.nodes.update().where(self.nodes.c.node == node)
724 2715ade4 Sofia Papagiannaki
        s = s.values(latest_version=serial)
725 585b75e7 Sofia Papagiannaki
        self.conn.execute(s).close()
726 2715ade4 Sofia Papagiannaki
727 0f510652 Sofia Papagiannaki
    def version_create(self, node, hash, size, type, source, muser, uuid,
728 0f510652 Sofia Papagiannaki
                       checksum, cluster=0,
729 876d7486 Sofia Papagiannaki
                       update_statistics_ancestors_depth=None,
730 e0525d86 Sofia Papagiannaki
                       available=True, map_check_timestamp=None):
731 4f917833 Sofia Papagiannaki
        """Create a new version from the given properties.
732 4f917833 Sofia Papagiannaki
           Return the (serial, mtime) of the new version.
733 4f917833 Sofia Papagiannaki
        """
734 2715ade4 Sofia Papagiannaki
735 4f917833 Sofia Papagiannaki
        mtime = time()
736 29148653 Sofia Papagiannaki
        s = self.versions.insert().values(
737 29148653 Sofia Papagiannaki
            node=node, hash=hash, size=size, type=type, source=source,
738 29148653 Sofia Papagiannaki
            mtime=mtime, muser=muser, uuid=uuid, checksum=checksum,
739 e0525d86 Sofia Papagiannaki
            cluster=cluster, available=available,
740 e0525d86 Sofia Papagiannaki
            map_check_timestamp=map_check_timestamp)
741 b43d44ad Sofia Papagiannaki
        serial = self.conn.execute(s).inserted_primary_key[0]
742 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster,
743 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
744 2715ade4 Sofia Papagiannaki
745 585b75e7 Sofia Papagiannaki
        self.nodes_set_latest_version(node, serial)
746 2715ade4 Sofia Papagiannaki
747 4f917833 Sofia Papagiannaki
        return serial, mtime
748 2715ade4 Sofia Papagiannaki
749 8221c89d Sofia Papagiannaki
    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
750 4f917833 Sofia Papagiannaki
        """Lookup the current version of the given node.
751 4f917833 Sofia Papagiannaki
           Return a list with its properties:
752 2715ade4 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime,
753 876d7486 Sofia Papagiannaki
            muser, uuid, checksum, cluster, available, map_check_timestamp)
754 4f917833 Sofia Papagiannaki
           or None if the current version is not found in the given cluster.
755 4f917833 Sofia Papagiannaki
        """
756 2715ade4 Sofia Papagiannaki
757 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
758 8221c89d Sofia Papagiannaki
        if not all_props:
759 8221c89d Sofia Papagiannaki
            s = select([v.c.serial])
760 8221c89d Sofia Papagiannaki
        else:
761 8221c89d Sofia Papagiannaki
            s = select([v.c.serial, v.c.node, v.c.hash,
762 8221c89d Sofia Papagiannaki
                        v.c.size, v.c.type, v.c.source,
763 8221c89d Sofia Papagiannaki
                        v.c.mtime, v.c.muser, v.c.uuid,
764 876d7486 Sofia Papagiannaki
                        v.c.checksum, v.c.cluster,
765 876d7486 Sofia Papagiannaki
                        v.c.available, v.c.map_check_timestamp])
766 62d938dc Sofia Papagiannaki
        if before != inf:
767 585b75e7 Sofia Papagiannaki
            c = select([func.max(self.versions.c.serial)],
768 2715ade4 Sofia Papagiannaki
                       self.versions.c.node == node)
769 62d938dc Sofia Papagiannaki
            c = c.where(self.versions.c.mtime < before)
770 585b75e7 Sofia Papagiannaki
        else:
771 585b75e7 Sofia Papagiannaki
            c = select([self.nodes.c.latest_version],
772 2715ade4 Sofia Papagiannaki
                       self.nodes.c.node == node)
773 b43d44ad Sofia Papagiannaki
        s = s.where(and_(v.c.serial == c,
774 b43d44ad Sofia Papagiannaki
                         v.c.cluster == cluster))
775 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
776 b43d44ad Sofia Papagiannaki
        props = r.fetchone()
777 b43d44ad Sofia Papagiannaki
        r.close()
778 70516d86 Sofia Papagiannaki
        if props:
779 4f917833 Sofia Papagiannaki
            return props
780 4f917833 Sofia Papagiannaki
        return None
781 2715ade4 Sofia Papagiannaki
782 29148653 Sofia Papagiannaki
    def version_lookup_bulk(self, nodes, before=inf, cluster=0,
783 ba402621 Sofia Papagiannaki
                            all_props=True, order_by_path=False):
784 8221c89d Sofia Papagiannaki
        """Lookup the current versions of the given nodes.
785 8221c89d Sofia Papagiannaki
           Return a list with their properties:
786 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
787 876d7486 Sofia Papagiannaki
            checksum, cluster, available, map_check_timestamp).
788 8221c89d Sofia Papagiannaki
        """
789 585b75e7 Sofia Papagiannaki
        if not nodes:
790 585b75e7 Sofia Papagiannaki
            return ()
791 8221c89d Sofia Papagiannaki
        v = self.versions.alias('v')
792 ba402621 Sofia Papagiannaki
        n = self.nodes.alias('n')
793 8221c89d Sofia Papagiannaki
        if not all_props:
794 8221c89d Sofia Papagiannaki
            s = select([v.c.serial])
795 8221c89d Sofia Papagiannaki
        else:
796 8221c89d Sofia Papagiannaki
            s = select([v.c.serial, v.c.node, v.c.hash,
797 8221c89d Sofia Papagiannaki
                        v.c.size, v.c.type, v.c.source,
798 8221c89d Sofia Papagiannaki
                        v.c.mtime, v.c.muser, v.c.uuid,
799 876d7486 Sofia Papagiannaki
                        v.c.checksum, v.c.cluster,
800 876d7486 Sofia Papagiannaki
                        v.c.available, v.c.map_check_timestamp])
801 8221c89d Sofia Papagiannaki
        if before != inf:
802 585b75e7 Sofia Papagiannaki
            c = select([func.max(self.versions.c.serial)],
803 2715ade4 Sofia Papagiannaki
                       self.versions.c.node.in_(nodes))
804 8221c89d Sofia Papagiannaki
            c = c.where(self.versions.c.mtime < before)
805 585b75e7 Sofia Papagiannaki
            c = c.group_by(self.versions.c.node)
806 585b75e7 Sofia Papagiannaki
        else:
807 585b75e7 Sofia Papagiannaki
            c = select([self.nodes.c.latest_version],
808 2715ade4 Sofia Papagiannaki
                       self.nodes.c.node.in_(nodes))
809 8221c89d Sofia Papagiannaki
        s = s.where(and_(v.c.serial.in_(c),
810 8221c89d Sofia Papagiannaki
                         v.c.cluster == cluster))
811 ba402621 Sofia Papagiannaki
        if order_by_path:
812 ba402621 Sofia Papagiannaki
            s = s.where(v.c.node == n.c.node)
813 ba402621 Sofia Papagiannaki
            s = s.order_by(n.c.path)
814 ba402621 Sofia Papagiannaki
        else:
815 ba402621 Sofia Papagiannaki
            s = s.order_by(v.c.node)
816 8221c89d Sofia Papagiannaki
        r = self.conn.execute(s)
817 8221c89d Sofia Papagiannaki
        rproxy = r.fetchall()
818 8221c89d Sofia Papagiannaki
        r.close()
819 8221c89d Sofia Papagiannaki
        return (tuple(row.values()) for row in rproxy)
820 2715ade4 Sofia Papagiannaki
821 d2fc71c9 Sofia Papagiannaki
    def version_get_properties(self, serial, keys=(), propnames=_propnames,
822 d2fc71c9 Sofia Papagiannaki
                               node=None):
823 4f917833 Sofia Papagiannaki
        """Return a sequence of values for the properties of
824 4f917833 Sofia Papagiannaki
           the version specified by serial and the keys, in the order given.
825 4f917833 Sofia Papagiannaki
           If keys is empty, return all properties in the order
826 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
827 876d7486 Sofia Papagiannaki
            checksum, cluster, available, map_check_timestamp).
828 4f917833 Sofia Papagiannaki
        """
829 2715ade4 Sofia Papagiannaki
830 b43d44ad Sofia Papagiannaki
        v = self.versions.alias()
831 37bee317 Antony Chazapis
        s = select([v.c.serial, v.c.node, v.c.hash,
832 cf341da4 Antony Chazapis
                    v.c.size, v.c.type, v.c.source,
833 cf341da4 Antony Chazapis
                    v.c.mtime, v.c.muser, v.c.uuid,
834 876d7486 Sofia Papagiannaki
                    v.c.checksum, v.c.cluster,
835 876d7486 Sofia Papagiannaki
                    v.c.available, v.c.map_check_timestamp],
836 876d7486 Sofia Papagiannaki
                   v.c.serial == serial)
837 d2fc71c9 Sofia Papagiannaki
        if node is not None:
838 d2fc71c9 Sofia Papagiannaki
            s = s.where(v.c.node == node)
839 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
840 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
841 b43d44ad Sofia Papagiannaki
        rp.close()
842 4f917833 Sofia Papagiannaki
        if r is None:
843 4f917833 Sofia Papagiannaki
            return r
844 2715ade4 Sofia Papagiannaki
845 4f917833 Sofia Papagiannaki
        if not keys:
846 4f917833 Sofia Papagiannaki
            return r
847 4f917833 Sofia Papagiannaki
        return [r[propnames[k]] for k in keys if k in propnames]
848 2715ade4 Sofia Papagiannaki
849 f7d5b0cf Antony Chazapis
    def version_put_property(self, serial, key, value):
850 f7d5b0cf Antony Chazapis
        """Set value for the property of version specified by key."""
851 2715ade4 Sofia Papagiannaki
852 f7d5b0cf Antony Chazapis
        if key not in _propnames:
853 f7d5b0cf Antony Chazapis
            return
854 f7d5b0cf Antony Chazapis
        s = self.versions.update()
855 f7d5b0cf Antony Chazapis
        s = s.where(self.versions.c.serial == serial)
856 f7d5b0cf Antony Chazapis
        s = s.values(**{key: value})
857 f7d5b0cf Antony Chazapis
        self.conn.execute(s).close()
858 2715ade4 Sofia Papagiannaki
859 0f510652 Sofia Papagiannaki
    def version_recluster(self, serial, cluster,
860 0f510652 Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
861 4f917833 Sofia Papagiannaki
        """Move the version into another cluster."""
862 2715ade4 Sofia Papagiannaki
863 4f917833 Sofia Papagiannaki
        props = self.version_get_properties(serial)
864 4f917833 Sofia Papagiannaki
        if not props:
865 4f917833 Sofia Papagiannaki
            return
866 4f917833 Sofia Papagiannaki
        node = props[NODE]
867 4f917833 Sofia Papagiannaki
        size = props[SIZE]
868 4f917833 Sofia Papagiannaki
        oldcluster = props[CLUSTER]
869 4f917833 Sofia Papagiannaki
        if cluster == oldcluster:
870 4f917833 Sofia Papagiannaki
            return
871 2715ade4 Sofia Papagiannaki
872 4f917833 Sofia Papagiannaki
        mtime = time()
873 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster,
874 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
875 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster,
876 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
877 2715ade4 Sofia Papagiannaki
878 b43d44ad Sofia Papagiannaki
        s = self.versions.update()
879 b43d44ad Sofia Papagiannaki
        s = s.where(self.versions.c.serial == serial)
880 2715ade4 Sofia Papagiannaki
        s = s.values(cluster=cluster)
881 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
882 2715ade4 Sofia Papagiannaki
883 0f510652 Sofia Papagiannaki
    def version_remove(self, serial, update_statistics_ancestors_depth=None):
884 4f917833 Sofia Papagiannaki
        """Remove the serial specified."""
885 2715ade4 Sofia Papagiannaki
886 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
887 4f917833 Sofia Papagiannaki
        if not props:
888 4f917833 Sofia Papagiannaki
            return
889 4f917833 Sofia Papagiannaki
        node = props[NODE]
890 5161c672 Antony Chazapis
        hash = props[HASH]
891 4f917833 Sofia Papagiannaki
        size = props[SIZE]
892 4f917833 Sofia Papagiannaki
        cluster = props[CLUSTER]
893 2715ade4 Sofia Papagiannaki
894 4f917833 Sofia Papagiannaki
        mtime = time()
895 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster,
896 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
897 2715ade4 Sofia Papagiannaki
898 b43d44ad Sofia Papagiannaki
        s = self.versions.delete().where(self.versions.c.serial == serial)
899 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
900 2715ade4 Sofia Papagiannaki
901 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
902 585b75e7 Sofia Papagiannaki
        if props:
903 eecad161 Sofia Papagiannaki
            self.nodes_set_latest_version(node, serial)
904 2715ade4 Sofia Papagiannaki
905 813e42e5 Antony Chazapis
        return hash, size
906 2715ade4 Sofia Papagiannaki
907 059857e2 Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
908 29148653 Sofia Papagiannaki
        """Return a list of (key, value) pairs of the specific version.
909 29148653 Sofia Papagiannaki

910 29148653 Sofia Papagiannaki
        If keys is empty, return all attributes.
911 29148653 Sofia Papagiannaki
        Othwerise, return only those specified.
912 4f917833 Sofia Papagiannaki
        """
913 2715ade4 Sofia Papagiannaki
914 4f917833 Sofia Papagiannaki
        if keys:
915 b43d44ad Sofia Papagiannaki
            attrs = self.attributes.alias()
916 b43d44ad Sofia Papagiannaki
            s = select([attrs.c.key, attrs.c.value])
917 b43d44ad Sofia Papagiannaki
            s = s.where(and_(attrs.c.key.in_(keys),
918 059857e2 Antony Chazapis
                             attrs.c.serial == serial,
919 059857e2 Antony Chazapis
                             attrs.c.domain == domain))
920 4f917833 Sofia Papagiannaki
        else:
921 b43d44ad Sofia Papagiannaki
            attrs = self.attributes.alias()
922 b43d44ad Sofia Papagiannaki
            s = select([attrs.c.key, attrs.c.value])
923 059857e2 Antony Chazapis
            s = s.where(and_(attrs.c.serial == serial,
924 059857e2 Antony Chazapis
                             attrs.c.domain == domain))
925 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
926 b43d44ad Sofia Papagiannaki
        l = r.fetchall()
927 b43d44ad Sofia Papagiannaki
        r.close()
928 b43d44ad Sofia Papagiannaki
        return l
929 2715ade4 Sofia Papagiannaki
930 4100e0ee Sofia Papagiannaki
    def attribute_set(self, serial, domain, node, items, is_latest=True):
931 4f917833 Sofia Papagiannaki
        """Set the attributes of the version specified by serial.
932 4f917833 Sofia Papagiannaki
           Receive attributes as an iterable of (key, value) pairs.
933 4f917833 Sofia Papagiannaki
        """
934 70516d86 Sofia Papagiannaki
        #insert or replace
935 70516d86 Sofia Papagiannaki
        #TODO better upsert
936 70516d86 Sofia Papagiannaki
        for k, v in items:
937 70516d86 Sofia Papagiannaki
            s = self.attributes.update()
938 70516d86 Sofia Papagiannaki
            s = s.where(and_(self.attributes.c.serial == serial,
939 059857e2 Antony Chazapis
                             self.attributes.c.domain == domain,
940 70516d86 Sofia Papagiannaki
                             self.attributes.c.key == k))
941 2715ade4 Sofia Papagiannaki
            s = s.values(value=v)
942 70516d86 Sofia Papagiannaki
            rp = self.conn.execute(s)
943 70516d86 Sofia Papagiannaki
            rp.close()
944 70516d86 Sofia Papagiannaki
            if rp.rowcount == 0:
945 70516d86 Sofia Papagiannaki
                s = self.attributes.insert()
946 4100e0ee Sofia Papagiannaki
                s = s.values(serial=serial, domain=domain, node=node,
947 4100e0ee Sofia Papagiannaki
                             is_latest=is_latest, key=k, value=v)
948 70516d86 Sofia Papagiannaki
                self.conn.execute(s).close()
949 2715ade4 Sofia Papagiannaki
950 059857e2 Antony Chazapis
    def attribute_del(self, serial, domain, keys=()):
951 4f917833 Sofia Papagiannaki
        """Delete attributes of the version specified by serial.
952 4f917833 Sofia Papagiannaki
           If keys is empty, delete all attributes.
953 4f917833 Sofia Papagiannaki
           Otherwise delete those specified.
954 4f917833 Sofia Papagiannaki
        """
955 2715ade4 Sofia Papagiannaki
956 4f917833 Sofia Papagiannaki
        if keys:
957 b43d44ad Sofia Papagiannaki
            #TODO more efficient way to do this?
958 b43d44ad Sofia Papagiannaki
            for key in keys:
959 b43d44ad Sofia Papagiannaki
                s = self.attributes.delete()
960 b43d44ad Sofia Papagiannaki
                s = s.where(and_(self.attributes.c.serial == serial,
961 059857e2 Antony Chazapis
                                 self.attributes.c.domain == domain,
962 b43d44ad Sofia Papagiannaki
                                 self.attributes.c.key == key))
963 b43d44ad Sofia Papagiannaki
                self.conn.execute(s).close()
964 4f917833 Sofia Papagiannaki
        else:
965 b43d44ad Sofia Papagiannaki
            s = self.attributes.delete()
966 059857e2 Antony Chazapis
            s = s.where(and_(self.attributes.c.serial == serial,
967 059857e2 Antony Chazapis
                             self.attributes.c.domain == domain))
968 b43d44ad Sofia Papagiannaki
            self.conn.execute(s).close()
969 2715ade4 Sofia Papagiannaki
970 4f917833 Sofia Papagiannaki
    def attribute_copy(self, source, dest):
971 2715ade4 Sofia Papagiannaki
        s = select(
972 4100e0ee Sofia Papagiannaki
            [dest, self.attributes.c.domain, self.attributes.c.node,
973 4100e0ee Sofia Papagiannaki
             self.attributes.c.key, self.attributes.c.value],
974 b43d44ad Sofia Papagiannaki
            self.attributes.c.serial == source)
975 1b61dbc0 Sofia Papagiannaki
        rp = self.conn.execute(s)
976 1b61dbc0 Sofia Papagiannaki
        attributes = rp.fetchall()
977 1b61dbc0 Sofia Papagiannaki
        rp.close()
978 4100e0ee Sofia Papagiannaki
        for dest, domain, node, k, v in attributes:
979 9f5c8386 Sofia Papagiannaki
            select_src_node = select(
980 9f5c8386 Sofia Papagiannaki
                [self.versions.c.node],
981 9f5c8386 Sofia Papagiannaki
                self.versions.c.serial == dest)
982 4100e0ee Sofia Papagiannaki
            # insert or replace
983 1b61dbc0 Sofia Papagiannaki
            s = self.attributes.update().where(and_(
984 1b61dbc0 Sofia Papagiannaki
                self.attributes.c.serial == dest,
985 059857e2 Antony Chazapis
                self.attributes.c.domain == domain,
986 1b61dbc0 Sofia Papagiannaki
                self.attributes.c.key == k))
987 96090728 Sofia Papagiannaki
            s = s.values(node=select_src_node, value=v)
988 4100e0ee Sofia Papagiannaki
            rp = self.conn.execute(s)
989 1b61dbc0 Sofia Papagiannaki
            rp.close()
990 1b61dbc0 Sofia Papagiannaki
            if rp.rowcount == 0:
991 1b61dbc0 Sofia Papagiannaki
                s = self.attributes.insert()
992 9f5c8386 Sofia Papagiannaki
                s = s.values(serial=dest, domain=domain, node=select_src_node,
993 4100e0ee Sofia Papagiannaki
                             is_latest=True, key=k, value=v)
994 4100e0ee Sofia Papagiannaki
            self.conn.execute(s).close()
995 4100e0ee Sofia Papagiannaki
996 4100e0ee Sofia Papagiannaki
    def attribute_unset_is_latest(self, node, exclude):
997 4100e0ee Sofia Papagiannaki
        u = self.attributes.update().where(and_(
998 4100e0ee Sofia Papagiannaki
            self.attributes.c.node == node,
999 29148653 Sofia Papagiannaki
            self.attributes.c.serial != exclude)).values({'is_latest': False})
1000 4100e0ee Sofia Papagiannaki
        self.conn.execute(u)
1001 2715ade4 Sofia Papagiannaki
1002 29148653 Sofia Papagiannaki
    def latest_attribute_keys(self, parent, domain, before=inf,
1003 29148653 Sofia Papagiannaki
                              except_cluster=0, pathq=None):
1004 4f917833 Sofia Papagiannaki
        """Return a list with all keys pairs defined
1005 4f917833 Sofia Papagiannaki
           for all latest versions under parent that
1006 4f917833 Sofia Papagiannaki
           do not belong to the cluster.
1007 4f917833 Sofia Papagiannaki
        """
1008 2715ade4 Sofia Papagiannaki
1009 78348987 Sofia Papagiannaki
        pathq = pathq or []
1010 78348987 Sofia Papagiannaki
1011 4f917833 Sofia Papagiannaki
        # TODO: Use another table to store before=inf results.
1012 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
1013 b83df225 Nanakos Chrysostomos
        s = select([self.attributes.c.key]).distinct()
1014 62d938dc Sofia Papagiannaki
        if before != inf:
1015 ae0a4858 Nanakos Chrysostomos
            filtered = select([func.max(v.c.serial)],
1016 ae0a4858 Nanakos Chrysostomos
                              and_(v.c.mtime < before,
1017 ae0a4858 Nanakos Chrysostomos
                                   v.c.node == self.versions.c.node))
1018 585b75e7 Sofia Papagiannaki
        else:
1019 585b75e7 Sofia Papagiannaki
            filtered = select([self.nodes.c.latest_version])
1020 cbe81673 Chrysostomos Nanakos
            filtered = filtered.where(self.nodes.c.node ==
1021 cbe81673 Chrysostomos Nanakos
                                      self.versions.c.node
1022 cbe81673 Chrysostomos Nanakos
                                      ).correlate(self.versions)
1023 b83df225 Nanakos Chrysostomos
        s = s.where(self.versions.c.serial == filtered)
1024 b83df225 Nanakos Chrysostomos
        s = s.where(self.versions.c.cluster != except_cluster)
1025 b83df225 Nanakos Chrysostomos
        s = s.where(self.versions.c.node.in_(select([self.nodes.c.node],
1026 cbe81673 Chrysostomos Nanakos
                                             self.nodes.c.parent == parent)))
1027 b83df225 Nanakos Chrysostomos
        s = s.where(self.attributes.c.serial == self.versions.c.serial)
1028 b83df225 Nanakos Chrysostomos
        s = s.where(self.attributes.c.domain == domain)
1029 b83df225 Nanakos Chrysostomos
        s = s.where(self.nodes.c.node == self.versions.c.node)
1030 ae0a4858 Nanakos Chrysostomos
        s = s.order_by(self.attributes.c.key)
1031 4a7b190f Nanakos Chrysostomos
        conja = []
1032 4a7b190f Nanakos Chrysostomos
        conjb = []
1033 cf341da4 Antony Chazapis
        for path, match in pathq:
1034 cf341da4 Antony Chazapis
            if match == MATCH_PREFIX:
1035 cbe81673 Chrysostomos Nanakos
                conja.append(self.nodes.c.path.like(self.escape_like(path) +
1036 cbe81673 Chrysostomos Nanakos
                                                    '%', escape=ESCAPE_CHAR))
1037 cf341da4 Antony Chazapis
            elif match == MATCH_EXACT:
1038 4a7b190f Nanakos Chrysostomos
                conjb.append(path)
1039 4a7b190f Nanakos Chrysostomos
        if conja or conjb:
1040 cbe81673 Chrysostomos Nanakos
            s = s.where(or_(self.nodes.c.path.in_(conjb), *conja))
1041 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
1042 70516d86 Sofia Papagiannaki
        rows = rp.fetchall()
1043 b43d44ad Sofia Papagiannaki
        rp.close()
1044 70516d86 Sofia Papagiannaki
        return [r[0] for r in rows]
1045 2715ade4 Sofia Papagiannaki
1046 4f917833 Sofia Papagiannaki
    def latest_version_list(self, parent, prefix='', delimiter=None,
1047 4f917833 Sofia Papagiannaki
                            start='', limit=10000, before=inf,
1048 371d907a Antony Chazapis
                            except_cluster=0, pathq=[], domain=None,
1049 371d907a Antony Chazapis
                            filterq=[], sizeq=None, all_props=False):
1050 4f917833 Sofia Papagiannaki
        """Return a (list of (path, serial) tuples, list of common prefixes)
1051 4f917833 Sofia Papagiannaki
           for the current versions of the paths with the given parent,
1052 4f917833 Sofia Papagiannaki
           matching the following criteria.
1053 2715ade4 Sofia Papagiannaki

1054 4f917833 Sofia Papagiannaki
           The property tuple for a version is returned if all
1055 4f917833 Sofia Papagiannaki
           of these conditions are true:
1056 2715ade4 Sofia Papagiannaki

1057 4f917833 Sofia Papagiannaki
                a. parent matches
1058 2715ade4 Sofia Papagiannaki

1059 4f917833 Sofia Papagiannaki
                b. path > start
1060 2715ade4 Sofia Papagiannaki

1061 4f917833 Sofia Papagiannaki
                c. path starts with prefix (and paths in pathq)
1062 2715ade4 Sofia Papagiannaki

1063 4f917833 Sofia Papagiannaki
                d. version is the max up to before
1064 2715ade4 Sofia Papagiannaki

1065 4f917833 Sofia Papagiannaki
                e. version is not in cluster
1066 2715ade4 Sofia Papagiannaki

1067 4f917833 Sofia Papagiannaki
                f. the path does not have the delimiter occuring
1068 4f917833 Sofia Papagiannaki
                   after the prefix, or ends with the delimiter
1069 2715ade4 Sofia Papagiannaki

1070 4f917833 Sofia Papagiannaki
                g. serial matches the attribute filter query.
1071 2715ade4 Sofia Papagiannaki

1072 4f917833 Sofia Papagiannaki
                   A filter query is a comma-separated list of
1073 4f917833 Sofia Papagiannaki
                   terms in one of these three forms:
1074 2715ade4 Sofia Papagiannaki

1075 4f917833 Sofia Papagiannaki
                   key
1076 4f917833 Sofia Papagiannaki
                       an attribute with this key must exist
1077 2715ade4 Sofia Papagiannaki

1078 4f917833 Sofia Papagiannaki
                   !key
1079 4f917833 Sofia Papagiannaki
                       an attribute with this key must not exist
1080 2715ade4 Sofia Papagiannaki

1081 4f917833 Sofia Papagiannaki
                   key ?op value
1082 4f917833 Sofia Papagiannaki
                       the attribute with this key satisfies the value
1083 4f917833 Sofia Papagiannaki
                       where ?op is one of ==, != <=, >=, <, >.
1084 2715ade4 Sofia Papagiannaki

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

1087 4f917833 Sofia Papagiannaki
           The list of common prefixes includes the prefixes
1088 4f917833 Sofia Papagiannaki
           matching up to the first delimiter after prefix,
1089 4f917833 Sofia Papagiannaki
           and are reported only once, as "virtual directories".
1090 4f917833 Sofia Papagiannaki
           The delimiter is included in the prefixes.
1091 2715ade4 Sofia Papagiannaki

1092 4f917833 Sofia Papagiannaki
           If arguments are None, then the corresponding matching rule
1093 4f917833 Sofia Papagiannaki
           will always match.
1094 2715ade4 Sofia Papagiannaki

1095 4f917833 Sofia Papagiannaki
           Limit applies to the first list of tuples returned.
1096 2715ade4 Sofia Papagiannaki

1097 29148653 Sofia Papagiannaki
           If all_props is True, return all properties after path,
1098 29148653 Sofia Papagiannaki
           not just serial.
1099 4f917833 Sofia Papagiannaki
        """
1100 2715ade4 Sofia Papagiannaki
1101 4f917833 Sofia Papagiannaki
        if not start or start < prefix:
1102 4f917833 Sofia Papagiannaki
            start = strprevling(prefix)
1103 4f917833 Sofia Papagiannaki
        nextling = strnextling(prefix)
1104 2715ade4 Sofia Papagiannaki
1105 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
1106 b43d44ad Sofia Papagiannaki
        n = self.nodes.alias('n')
1107 62d938dc Sofia Papagiannaki
        if before != inf:
1108 ae0a4858 Nanakos Chrysostomos
            filtered = select([func.max(v.c.serial)],
1109 ae0a4858 Nanakos Chrysostomos
                              and_(v.c.mtime < before,
1110 ae0a4858 Nanakos Chrysostomos
                                   v.c.node == self.versions.c.node))
1111 76da16c3 Nanakos Chrysostomos
            inner_join = \
1112 cbe81673 Chrysostomos Nanakos
                self.nodes.join(self.versions,
1113 cbe81673 Chrysostomos Nanakos
                                onclause=self.versions.c.serial == filtered)
1114 585b75e7 Sofia Papagiannaki
        else:
1115 585b75e7 Sofia Papagiannaki
            filtered = select([self.nodes.c.latest_version])
1116 cbe81673 Chrysostomos Nanakos
            filtered = filtered.where(self.nodes.c.node ==
1117 cbe81673 Chrysostomos Nanakos
                                      self.versions.c.node
1118 cbe81673 Chrysostomos Nanakos
                                      ).correlate(self.versions)
1119 76da16c3 Nanakos Chrysostomos
            inner_join = \
1120 cbe81673 Chrysostomos Nanakos
                self.nodes.join(self.versions,
1121 cbe81673 Chrysostomos Nanakos
                                onclause=
1122 cbe81673 Chrysostomos Nanakos
                                self.versions.c.serial == filtered)
1123 76da16c3 Nanakos Chrysostomos
        if not all_props:
1124 76da16c3 Nanakos Chrysostomos
            s = select([self.nodes.c.path,
1125 cbe81673 Chrysostomos Nanakos
                       self.versions.c.serial],
1126 cbe81673 Chrysostomos Nanakos
                       from_obj=[inner_join]).distinct()
1127 76da16c3 Nanakos Chrysostomos
        else:
1128 76da16c3 Nanakos Chrysostomos
            s = select([self.nodes.c.path,
1129 cbe81673 Chrysostomos Nanakos
                       self.versions.c.serial, self.versions.c.node,
1130 cbe81673 Chrysostomos Nanakos
                       self.versions.c.hash,
1131 cbe81673 Chrysostomos Nanakos
                       self.versions.c.size, self.versions.c.type,
1132 cbe81673 Chrysostomos Nanakos
                       self.versions.c.source,
1133 cbe81673 Chrysostomos Nanakos
                       self.versions.c.mtime, self.versions.c.muser,
1134 cbe81673 Chrysostomos Nanakos
                       self.versions.c.uuid,
1135 cbe81673 Chrysostomos Nanakos
                       self.versions.c.checksum,
1136 6e3e5c84 Sofia Papagiannaki
                       self.versions.c.cluster,
1137 6e3e5c84 Sofia Papagiannaki
                       self.versions.c.available,
1138 6e3e5c84 Sofia Papagiannaki
                       self.versions.c.map_check_timestamp],
1139 cbe81673 Chrysostomos Nanakos
                       from_obj=[inner_join]).distinct()
1140 76da16c3 Nanakos Chrysostomos
1141 76da16c3 Nanakos Chrysostomos
        s = s.where(self.versions.c.cluster != except_cluster)
1142 76da16c3 Nanakos Chrysostomos
        s = s.where(self.versions.c.node.in_(select([self.nodes.c.node],
1143 b83df225 Nanakos Chrysostomos
                                             self.nodes.c.parent == parent)))
1144 2715ade4 Sofia Papagiannaki
1145 e98c5561 Nanakos Chrysostomos
        s = s.where(self.versions.c.node == self.nodes.c.node)
1146 b83df225 Nanakos Chrysostomos
        s = s.where(and_(self.nodes.c.path > bindparam('start'),
1147 cbe81673 Chrysostomos Nanakos
                    self.nodes.c.path < nextling))
1148 4a7b190f Nanakos Chrysostomos
        conja = []
1149 4a7b190f Nanakos Chrysostomos
        conjb = []
1150 cf341da4 Antony Chazapis
        for path, match in pathq:
1151 cf341da4 Antony Chazapis
            if match == MATCH_PREFIX:
1152 cbe81673 Chrysostomos Nanakos
                conja.append(self.nodes.c.path.like(self.escape_like(path) +
1153 cbe81673 Chrysostomos Nanakos
                             '%', escape=ESCAPE_CHAR))
1154 cf341da4 Antony Chazapis
            elif match == MATCH_EXACT:
1155 4a7b190f Nanakos Chrysostomos
                conjb.append(path)
1156 4a7b190f Nanakos Chrysostomos
        if conja or conjb:
1157 cbe81673 Chrysostomos Nanakos
            s = s.where(or_(self.nodes.c.path.in_(conjb), *conja))
1158 2715ade4 Sofia Papagiannaki
1159 7ff57991 Antony Chazapis
        if sizeq and len(sizeq) == 2:
1160 7ff57991 Antony Chazapis
            if sizeq[0]:
1161 76da16c3 Nanakos Chrysostomos
                s = s.where(self.versions.c.size >= sizeq[0])
1162 7ff57991 Antony Chazapis
            if sizeq[1]:
1163 76da16c3 Nanakos Chrysostomos
                s = s.where(self.versions.c.size < sizeq[1])
1164 2715ade4 Sofia Papagiannaki
1165 9d41b050 chazapis
        if domain and filterq:
1166 a847494c chazapis
            a = self.attributes.alias('a')
1167 3d13f97a Sofia Papagiannaki
            included, excluded, opers = parse_filters(filterq)
1168 3d13f97a Sofia Papagiannaki
            if included:
1169 a847494c chazapis
                subs = select([1])
1170 cbe81673 Chrysostomos Nanakos
                subs = subs.where(self.attributes.c.serial ==
1171 cbe81673 Chrysostomos Nanakos
                                  self.versions.c.serial
1172 cbe81673 Chrysostomos Nanakos
                                  ).correlate(self.versions)
1173 b83df225 Nanakos Chrysostomos
                subs = subs.where(self.attributes.c.domain == domain)
1174 cbe81673 Chrysostomos Nanakos
                subs = subs.where(or_(*[self.attributes.c.key.op('=')(x)
1175 cbe81673 Chrysostomos Nanakos
                                  for x in included]))
1176 a847494c chazapis
                s = s.where(exists(subs))
1177 3d13f97a Sofia Papagiannaki
            if excluded:
1178 a847494c chazapis
                subs = select([1])
1179 cbe81673 Chrysostomos Nanakos
                subs = subs.where(self.attributes.c.serial ==
1180 cbe81673 Chrysostomos Nanakos
                                  self.versions.c.serial
1181 cbe81673 Chrysostomos Nanakos
                                  ).correlate(self.versions)
1182 b83df225 Nanakos Chrysostomos
                subs = subs.where(self.attributes.c.domain == domain)
1183 cbe81673 Chrysostomos Nanakos
                subs = subs.where(or_(*[self.attributes.c.key.op('=')(x)
1184 cbe81673 Chrysostomos Nanakos
                                  for x in excluded]))
1185 a847494c chazapis
                s = s.where(not_(exists(subs)))
1186 9d41b050 chazapis
            if opers:
1187 6b2f11b4 Antony Chazapis
                for k, o, val in opers:
1188 6b2f11b4 Antony Chazapis
                    subs = select([1])
1189 cbe81673 Chrysostomos Nanakos
                    subs = subs.where(self.attributes.c.serial ==
1190 cbe81673 Chrysostomos Nanakos
                                      self.versions.c.serial
1191 cbe81673 Chrysostomos Nanakos
                                      ).correlate(self.versions)
1192 b83df225 Nanakos Chrysostomos
                    subs = subs.where(self.attributes.c.domain == domain)
1193 2715ade4 Sofia Papagiannaki
                    subs = subs.where(
1194 cbe81673 Chrysostomos Nanakos
                        and_(self.attributes.c.key.op('=')(k),
1195 cbe81673 Chrysostomos Nanakos
                             self.attributes.c.value.op(o)(val)))
1196 6b2f11b4 Antony Chazapis
                    s = s.where(exists(subs))
1197 2715ade4 Sofia Papagiannaki
1198 76da16c3 Nanakos Chrysostomos
        s = s.order_by(self.nodes.c.path)
1199 2715ade4 Sofia Papagiannaki
1200 4f917833 Sofia Papagiannaki
        if not delimiter:
1201 b43d44ad Sofia Papagiannaki
            s = s.limit(limit)
1202 b43d44ad Sofia Papagiannaki
            rp = self.conn.execute(s, start=start)
1203 9d41b050 chazapis
            r = rp.fetchall()
1204 b43d44ad Sofia Papagiannaki
            rp.close()
1205 b43d44ad Sofia Papagiannaki
            return r, ()
1206 2715ade4 Sofia Papagiannaki
1207 4f917833 Sofia Papagiannaki
        pfz = len(prefix)
1208 4f917833 Sofia Papagiannaki
        dz = len(delimiter)
1209 4f917833 Sofia Papagiannaki
        count = 0
1210 4f917833 Sofia Papagiannaki
        prefixes = []
1211 4f917833 Sofia Papagiannaki
        pappend = prefixes.append
1212 4f917833 Sofia Papagiannaki
        matches = []
1213 4f917833 Sofia Papagiannaki
        mappend = matches.append
1214 2715ade4 Sofia Papagiannaki
1215 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s, start=start)
1216 4f917833 Sofia Papagiannaki
        while True:
1217 b43d44ad Sofia Papagiannaki
            props = rp.fetchone()
1218 4f917833 Sofia Papagiannaki
            if props is None:
1219 4f917833 Sofia Papagiannaki
                break
1220 371d907a Antony Chazapis
            path = props[0]
1221 4f917833 Sofia Papagiannaki
            idx = path.find(delimiter, pfz)
1222 2715ade4 Sofia Papagiannaki
1223 4f917833 Sofia Papagiannaki
            if idx < 0:
1224 4f917833 Sofia Papagiannaki
                mappend(props)
1225 4f917833 Sofia Papagiannaki
                count += 1
1226 4f917833 Sofia Papagiannaki
                if count >= limit:
1227 4f917833 Sofia Papagiannaki
                    break
1228 4f917833 Sofia Papagiannaki
                continue
1229 2715ade4 Sofia Papagiannaki
1230 4f917833 Sofia Papagiannaki
            if idx + dz == len(path):
1231 4f917833 Sofia Papagiannaki
                mappend(props)
1232 4f917833 Sofia Papagiannaki
                count += 1
1233 2715ade4 Sofia Papagiannaki
                continue  # Get one more, in case there is a path.
1234 d3be972a Sofia Papagiannaki
            pf = path[:idx + dz]
1235 d3be972a Sofia Papagiannaki
            pappend(pf)
1236 2715ade4 Sofia Papagiannaki
            if count >= limit:
1237 4f917833 Sofia Papagiannaki
                break
1238 2715ade4 Sofia Papagiannaki
1239 2715ade4 Sofia Papagiannaki
            rp = self.conn.execute(s, start=strnextling(pf))  # New start.
1240 9eb713e1 Sofia Papagiannaki
        rp.close()
1241 2715ade4 Sofia Papagiannaki
1242 4f917833 Sofia Papagiannaki
        return matches, prefixes
1243 2715ade4 Sofia Papagiannaki
1244 2bbf1544 Georgios D. Tsoukalas
    def latest_uuid(self, uuid, cluster):
1245 2bbf1544 Georgios D. Tsoukalas
        """Return the latest version of the given uuid and cluster.
1246 2bbf1544 Georgios D. Tsoukalas

1247 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1248 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1249 2bbf1544 Georgios D. Tsoukalas

1250 2bbf1544 Georgios D. Tsoukalas
        """
1251 2715ade4 Sofia Papagiannaki
1252 37bee317 Antony Chazapis
        v = self.versions.alias('v')
1253 37bee317 Antony Chazapis
        n = self.nodes.alias('n')
1254 37bee317 Antony Chazapis
        s = select([n.c.path, v.c.serial])
1255 37bee317 Antony Chazapis
        filtered = select([func.max(self.versions.c.serial)])
1256 2bbf1544 Georgios D. Tsoukalas
        filtered = filtered.where(self.versions.c.uuid == uuid)
1257 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
1258 2bbf1544 Georgios D. Tsoukalas
            filtered = filtered.where(self.versions.c.cluster == cluster)
1259 2bbf1544 Georgios D. Tsoukalas
        s = s.where(v.c.serial == filtered)
1260 37bee317 Antony Chazapis
        s = s.where(n.c.node == v.c.node)
1261 2715ade4 Sofia Papagiannaki
1262 37bee317 Antony Chazapis
        r = self.conn.execute(s)
1263 37bee317 Antony Chazapis
        l = r.fetchone()
1264 37bee317 Antony Chazapis
        r.close()
1265 37bee317 Antony Chazapis
        return l
1266 5576e6dd Sofia Papagiannaki
1267 8b365874 Sofia Papagiannaki
    def domain_object_list(self, domain, paths, cluster=None):
1268 5576e6dd Sofia Papagiannaki
        """Return a list of (path, property list, attribute dictionary)
1269 5576e6dd Sofia Papagiannaki
           for the objects in the specific domain and cluster.
1270 5576e6dd Sofia Papagiannaki
        """
1271 5576e6dd Sofia Papagiannaki
1272 5576e6dd Sofia Papagiannaki
        v = self.versions.alias('v')
1273 5576e6dd Sofia Papagiannaki
        n = self.nodes.alias('n')
1274 5576e6dd Sofia Papagiannaki
        a = self.attributes.alias('a')
1275 5576e6dd Sofia Papagiannaki
1276 5576e6dd Sofia Papagiannaki
        s = select([n.c.path, v.c.serial, v.c.node, v.c.hash, v.c.size,
1277 5576e6dd Sofia Papagiannaki
                    v.c.type, v.c.source, v.c.mtime, v.c.muser, v.c.uuid,
1278 5576e6dd Sofia Papagiannaki
                    v.c.checksum, v.c.cluster, a.c.key, a.c.value])
1279 5576e6dd Sofia Papagiannaki
        if cluster:
1280 5576e6dd Sofia Papagiannaki
            s = s.where(v.c.cluster == cluster)
1281 5576e6dd Sofia Papagiannaki
        s = s.where(v.c.serial == a.c.serial)
1282 5576e6dd Sofia Papagiannaki
        s = s.where(a.c.domain == domain)
1283 4100e0ee Sofia Papagiannaki
        s = s.where(a.c.node == n.c.node)
1284 fbafd482 Sofia Papagiannaki
        s = s.where(a.c.is_latest == True)
1285 8b365874 Sofia Papagiannaki
        if paths:
1286 8b365874 Sofia Papagiannaki
            s = s.where(n.c.path.in_(paths))
1287 5576e6dd Sofia Papagiannaki
1288 5576e6dd Sofia Papagiannaki
        r = self.conn.execute(s)
1289 5576e6dd Sofia Papagiannaki
        rows = r.fetchall()
1290 5576e6dd Sofia Papagiannaki
        r.close()
1291 5576e6dd Sofia Papagiannaki
1292 5576e6dd Sofia Papagiannaki
        group_by = itemgetter(slice(12))
1293 29148653 Sofia Papagiannaki
        rows.sort(key=group_by)
1294 5576e6dd Sofia Papagiannaki
        groups = groupby(rows, group_by)
1295 29148653 Sofia Papagiannaki
        return [(k[0], k[1:], dict([i[12:] for i in data])) for
1296 29148653 Sofia Papagiannaki
                (k, data) in groups]
1297 dc88754b Nanakos Chrysostomos
1298 dc88754b Nanakos Chrysostomos
    def get_props(self, paths):
1299 dc88754b Nanakos Chrysostomos
        inner_join = \
1300 dc88754b Nanakos Chrysostomos
            self.nodes.join(self.versions,
1301 cbe81673 Chrysostomos Nanakos
                            onclause=self.versions.c.serial ==
1302 cbe81673 Chrysostomos Nanakos
                            self.nodes.c.latest_version)
1303 dc88754b Nanakos Chrysostomos
        cc = self.nodes.c.path.in_(paths)
1304 7fef13f0 Nanakos Chrysostomos
        s = select([self.nodes.c.path, self.versions.c.type],
1305 cbe81673 Chrysostomos Nanakos
                   from_obj=[inner_join]).where(cc).distinct()
1306 dc88754b Nanakos Chrysostomos
        r = self.conn.execute(s)
1307 dc88754b Nanakos Chrysostomos
        rows = r.fetchall()
1308 dc88754b Nanakos Chrysostomos
        r.close()
1309 dc88754b Nanakos Chrysostomos
        if rows:
1310 dc88754b Nanakos Chrysostomos
            return rows
1311 dc88754b Nanakos Chrysostomos
        return None