Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / lib / sqlalchemy / node.py @ 4a7b190f

History | View | Annotate | Download (47.8 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 2715ade4 Sofia Papagiannaki
 CLUSTER) = range(11)
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 4f917833 Sofia Papagiannaki
}
107 4f917833 Sofia Papagiannaki
108 2715ade4 Sofia Papagiannaki
109 6a82f89f Sofia Papagiannaki
def create_tables(engine):
110 6a82f89f Sofia Papagiannaki
    metadata = MetaData()
111 2715ade4 Sofia Papagiannaki
112 6a82f89f Sofia Papagiannaki
    #create nodes table
113 2715ade4 Sofia Papagiannaki
    columns = []
114 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer, primary_key=True))
115 6a82f89f Sofia Papagiannaki
    columns.append(Column('parent', Integer,
116 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
117 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
118 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
119 6a82f89f Sofia Papagiannaki
                          autoincrement=False))
120 6a82f89f Sofia Papagiannaki
    columns.append(Column('latest_version', Integer))
121 6a82f89f Sofia Papagiannaki
    columns.append(Column('path', String(2048), default='', nullable=False))
122 6a82f89f Sofia Papagiannaki
    nodes = Table('nodes', metadata, *columns, mysql_engine='InnoDB')
123 6a82f89f Sofia Papagiannaki
    Index('idx_nodes_path', nodes.c.path, unique=True)
124 6a82f89f Sofia Papagiannaki
    Index('idx_nodes_parent', nodes.c.parent)
125 2715ade4 Sofia Papagiannaki
126 6a82f89f Sofia Papagiannaki
    #create policy table
127 2715ade4 Sofia Papagiannaki
    columns = []
128 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
129 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
130 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
131 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
132 6a82f89f Sofia Papagiannaki
                          primary_key=True))
133 6a82f89f Sofia Papagiannaki
    columns.append(Column('key', String(128), primary_key=True))
134 6a82f89f Sofia Papagiannaki
    columns.append(Column('value', String(256)))
135 29148653 Sofia Papagiannaki
    Table('policy', metadata, *columns, mysql_engine='InnoDB')
136 2715ade4 Sofia Papagiannaki
137 6a82f89f Sofia Papagiannaki
    #create statistics table
138 2715ade4 Sofia Papagiannaki
    columns = []
139 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
140 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
141 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
142 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
143 6a82f89f Sofia Papagiannaki
                          primary_key=True))
144 6a82f89f Sofia Papagiannaki
    columns.append(Column('population', Integer, nullable=False, default=0))
145 6a82f89f Sofia Papagiannaki
    columns.append(Column('size', BigInteger, nullable=False, default=0))
146 6a82f89f Sofia Papagiannaki
    columns.append(Column('mtime', DECIMAL(precision=16, scale=6)))
147 6a82f89f Sofia Papagiannaki
    columns.append(Column('cluster', Integer, nullable=False, default=0,
148 6a82f89f Sofia Papagiannaki
                          primary_key=True, autoincrement=False))
149 29148653 Sofia Papagiannaki
    Table('statistics', metadata, *columns, mysql_engine='InnoDB')
150 2715ade4 Sofia Papagiannaki
151 6a82f89f Sofia Papagiannaki
    #create versions table
152 2715ade4 Sofia Papagiannaki
    columns = []
153 6a82f89f Sofia Papagiannaki
    columns.append(Column('serial', Integer, primary_key=True))
154 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
155 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
156 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
157 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE')))
158 6a82f89f Sofia Papagiannaki
    columns.append(Column('hash', String(256)))
159 6a82f89f Sofia Papagiannaki
    columns.append(Column('size', BigInteger, nullable=False, default=0))
160 6a82f89f Sofia Papagiannaki
    columns.append(Column('type', String(256), nullable=False, default=''))
161 6a82f89f Sofia Papagiannaki
    columns.append(Column('source', Integer))
162 6a82f89f Sofia Papagiannaki
    columns.append(Column('mtime', DECIMAL(precision=16, scale=6)))
163 6a82f89f Sofia Papagiannaki
    columns.append(Column('muser', String(256), nullable=False, default=''))
164 6a82f89f Sofia Papagiannaki
    columns.append(Column('uuid', String(64), nullable=False, default=''))
165 6a82f89f Sofia Papagiannaki
    columns.append(Column('checksum', String(256), nullable=False, default=''))
166 6a82f89f Sofia Papagiannaki
    columns.append(Column('cluster', Integer, nullable=False, default=0))
167 6a82f89f Sofia Papagiannaki
    versions = Table('versions', metadata, *columns, mysql_engine='InnoDB')
168 6a82f89f Sofia Papagiannaki
    Index('idx_versions_node_mtime', versions.c.node, versions.c.mtime)
169 6a82f89f Sofia Papagiannaki
    Index('idx_versions_node_uuid', versions.c.uuid)
170 2715ade4 Sofia Papagiannaki
171 6a82f89f Sofia Papagiannaki
    #create attributes table
172 6a82f89f Sofia Papagiannaki
    columns = []
173 6a82f89f Sofia Papagiannaki
    columns.append(Column('serial', Integer,
174 6a82f89f Sofia Papagiannaki
                          ForeignKey('versions.serial',
175 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
176 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
177 6a82f89f Sofia Papagiannaki
                          primary_key=True))
178 6a82f89f Sofia Papagiannaki
    columns.append(Column('domain', String(256), primary_key=True))
179 6a82f89f Sofia Papagiannaki
    columns.append(Column('key', String(128), primary_key=True))
180 6a82f89f Sofia Papagiannaki
    columns.append(Column('value', String(256)))
181 4100e0ee Sofia Papagiannaki
    columns.append(Column('node', Integer, nullable=False, default=0))
182 4100e0ee Sofia Papagiannaki
    columns.append(Column('is_latest', Boolean, nullable=False, default=True))
183 6a82f89f Sofia Papagiannaki
    attributes = Table('attributes', metadata, *columns, mysql_engine='InnoDB')
184 5576e6dd Sofia Papagiannaki
    Index('idx_attributes_domain', attributes.c.domain)
185 4100e0ee Sofia Papagiannaki
    Index('idx_attributes_serial_node', attributes.c.serial, attributes.c.node)
186 2715ade4 Sofia Papagiannaki
187 6a82f89f Sofia Papagiannaki
    metadata.create_all(engine)
188 6a82f89f Sofia Papagiannaki
    return metadata.sorted_tables
189 4f917833 Sofia Papagiannaki
190 2715ade4 Sofia Papagiannaki
191 4f917833 Sofia Papagiannaki
class Node(DBWorker):
192 4f917833 Sofia Papagiannaki
    """Nodes store path organization and have multiple versions.
193 4f917833 Sofia Papagiannaki
       Versions store object history and have multiple attributes.
194 4f917833 Sofia Papagiannaki
       Attributes store metadata.
195 4f917833 Sofia Papagiannaki
    """
196 2715ade4 Sofia Papagiannaki
197 4f917833 Sofia Papagiannaki
    # TODO: Provide an interface for included and excluded clusters.
198 2715ade4 Sofia Papagiannaki
199 4f917833 Sofia Papagiannaki
    def __init__(self, **params):
200 4f917833 Sofia Papagiannaki
        DBWorker.__init__(self, **params)
201 6a82f89f Sofia Papagiannaki
        try:
202 6a82f89f Sofia Papagiannaki
            metadata = MetaData(self.engine)
203 6a82f89f Sofia Papagiannaki
            self.nodes = Table('nodes', metadata, autoload=True)
204 6a82f89f Sofia Papagiannaki
            self.policy = Table('policy', metadata, autoload=True)
205 6a82f89f Sofia Papagiannaki
            self.statistics = Table('statistics', metadata, autoload=True)
206 6a82f89f Sofia Papagiannaki
            self.versions = Table('versions', metadata, autoload=True)
207 6a82f89f Sofia Papagiannaki
            self.attributes = Table('attributes', metadata, autoload=True)
208 6a82f89f Sofia Papagiannaki
        except NoSuchTableError:
209 6a82f89f Sofia Papagiannaki
            tables = create_tables(self.engine)
210 6a82f89f Sofia Papagiannaki
            map(lambda t: self.__setattr__(t.name, t), tables)
211 2715ade4 Sofia Papagiannaki
212 d3be972a Sofia Papagiannaki
        s = self.nodes.select().where(and_(self.nodes.c.node == ROOTNODE,
213 d3be972a Sofia Papagiannaki
                                           self.nodes.c.parent == ROOTNODE))
214 26837206 Georgios D. Tsoukalas
        wrapper = self.wrapper
215 26837206 Georgios D. Tsoukalas
        wrapper.execute()
216 26837206 Georgios D. Tsoukalas
        try:
217 26837206 Georgios D. Tsoukalas
            rp = self.conn.execute(s)
218 26837206 Georgios D. Tsoukalas
            r = rp.fetchone()
219 26837206 Georgios D. Tsoukalas
            rp.close()
220 26837206 Georgios D. Tsoukalas
            if not r:
221 26837206 Georgios D. Tsoukalas
                s = self.nodes.insert(
222 26837206 Georgios D. Tsoukalas
                ).values(node=ROOTNODE, parent=ROOTNODE, path='')
223 26837206 Georgios D. Tsoukalas
                self.conn.execute(s)
224 26837206 Georgios D. Tsoukalas
        finally:
225 26837206 Georgios D. Tsoukalas
            wrapper.commit()
226 2715ade4 Sofia Papagiannaki
227 4f917833 Sofia Papagiannaki
    def node_create(self, parent, path):
228 4f917833 Sofia Papagiannaki
        """Create a new node from the given properties.
229 4f917833 Sofia Papagiannaki
           Return the node identifier of the new node.
230 4f917833 Sofia Papagiannaki
        """
231 18266c93 Sofia Papagiannaki
        #TODO catch IntegrityError?
232 18266c93 Sofia Papagiannaki
        s = self.nodes.insert().values(parent=parent, path=path)
233 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
234 18266c93 Sofia Papagiannaki
        inserted_primary_key = r.inserted_primary_key[0]
235 18266c93 Sofia Papagiannaki
        r.close()
236 18266c93 Sofia Papagiannaki
        return inserted_primary_key
237 2715ade4 Sofia Papagiannaki
238 985b9b09 Sofia Papagiannaki
    def node_lookup(self, path, for_update=False):
239 4f917833 Sofia Papagiannaki
        """Lookup the current node of the given path.
240 4f917833 Sofia Papagiannaki
           Return None if the path is not found.
241 4f917833 Sofia Papagiannaki
        """
242 2715ade4 Sofia Papagiannaki
243 6b20cfbc Antony Chazapis
        # Use LIKE for comparison to avoid MySQL problems with trailing spaces.
244 96090728 Sofia Papagiannaki
        s = select([self.nodes.c.node],
245 96090728 Sofia Papagiannaki
                   self.nodes.c.path.like(self.escape_like(path),
246 96090728 Sofia Papagiannaki
                                          escape=ESCAPE_CHAR),
247 96090728 Sofia Papagiannaki
                   for_update=for_update)
248 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
249 18266c93 Sofia Papagiannaki
        row = r.fetchone()
250 18266c93 Sofia Papagiannaki
        r.close()
251 18266c93 Sofia Papagiannaki
        if row:
252 18266c93 Sofia Papagiannaki
            return row[0]
253 4f917833 Sofia Papagiannaki
        return None
254 2715ade4 Sofia Papagiannaki
255 8221c89d Sofia Papagiannaki
    def node_lookup_bulk(self, paths):
256 8221c89d Sofia Papagiannaki
        """Lookup the current nodes for the given paths.
257 8221c89d Sofia Papagiannaki
           Return () if the path is not found.
258 8221c89d Sofia Papagiannaki
        """
259 2715ade4 Sofia Papagiannaki
260 c53502b1 Sofia Papagiannaki
        if not paths:
261 c53502b1 Sofia Papagiannaki
            return ()
262 8221c89d Sofia Papagiannaki
        # Use LIKE for comparison to avoid MySQL problems with trailing spaces.
263 8221c89d Sofia Papagiannaki
        s = select([self.nodes.c.node], self.nodes.c.path.in_(paths))
264 8221c89d Sofia Papagiannaki
        r = self.conn.execute(s)
265 8221c89d Sofia Papagiannaki
        rows = r.fetchall()
266 8221c89d Sofia Papagiannaki
        r.close()
267 8221c89d Sofia Papagiannaki
        return [row[0] for row in rows]
268 2715ade4 Sofia Papagiannaki
269 4f917833 Sofia Papagiannaki
    def node_get_properties(self, node):
270 4f917833 Sofia Papagiannaki
        """Return the node's (parent, path).
271 4f917833 Sofia Papagiannaki
           Return None if the node is not found.
272 4f917833 Sofia Papagiannaki
        """
273 2715ade4 Sofia Papagiannaki
274 18266c93 Sofia Papagiannaki
        s = select([self.nodes.c.parent, self.nodes.c.path])
275 18266c93 Sofia Papagiannaki
        s = s.where(self.nodes.c.node == node)
276 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
277 18266c93 Sofia Papagiannaki
        l = r.fetchone()
278 18266c93 Sofia Papagiannaki
        r.close()
279 18266c93 Sofia Papagiannaki
        return l
280 2715ade4 Sofia Papagiannaki
281 4f917833 Sofia Papagiannaki
    def node_get_versions(self, node, keys=(), propnames=_propnames):
282 4f917833 Sofia Papagiannaki
        """Return the properties of all versions at node.
283 4f917833 Sofia Papagiannaki
           If keys is empty, return all properties in the order
284 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
285 29148653 Sofia Papagiannaki
            checksum, cluster).
286 4f917833 Sofia Papagiannaki
        """
287 2715ade4 Sofia Papagiannaki
288 1c2fc0ff Antony Chazapis
        s = select([self.versions.c.serial,
289 1c2fc0ff Antony Chazapis
                    self.versions.c.node,
290 1c2fc0ff Antony Chazapis
                    self.versions.c.hash,
291 1c2fc0ff Antony Chazapis
                    self.versions.c.size,
292 cf341da4 Antony Chazapis
                    self.versions.c.type,
293 1c2fc0ff Antony Chazapis
                    self.versions.c.source,
294 1c2fc0ff Antony Chazapis
                    self.versions.c.mtime,
295 1c2fc0ff Antony Chazapis
                    self.versions.c.muser,
296 25ae8b75 Antony Chazapis
                    self.versions.c.uuid,
297 f7d5b0cf Antony Chazapis
                    self.versions.c.checksum,
298 1c2fc0ff Antony Chazapis
                    self.versions.c.cluster], self.versions.c.node == node)
299 cc28f894 Sofia Papagiannaki
        s = s.order_by(self.versions.c.serial)
300 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
301 18266c93 Sofia Papagiannaki
        rows = r.fetchall()
302 9eb713e1 Sofia Papagiannaki
        r.close()
303 18266c93 Sofia Papagiannaki
        if not rows:
304 18266c93 Sofia Papagiannaki
            return rows
305 2715ade4 Sofia Papagiannaki
306 4f917833 Sofia Papagiannaki
        if not keys:
307 18266c93 Sofia Papagiannaki
            return rows
308 2715ade4 Sofia Papagiannaki
309 29148653 Sofia Papagiannaki
        return [[p[propnames[k]] for k in keys if k in propnames] for
310 29148653 Sofia Papagiannaki
                p in rows]
311 2715ade4 Sofia Papagiannaki
312 4f917833 Sofia Papagiannaki
    def node_count_children(self, node):
313 4f917833 Sofia Papagiannaki
        """Return node's child count."""
314 2715ade4 Sofia Papagiannaki
315 18266c93 Sofia Papagiannaki
        s = select([func.count(self.nodes.c.node)])
316 18266c93 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.parent == node,
317 18266c93 Sofia Papagiannaki
                         self.nodes.c.node != ROOTNODE))
318 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
319 18266c93 Sofia Papagiannaki
        row = r.fetchone()
320 18266c93 Sofia Papagiannaki
        r.close()
321 18266c93 Sofia Papagiannaki
        return row[0]
322 2715ade4 Sofia Papagiannaki
323 0f510652 Sofia Papagiannaki
    def node_purge_children(self, parent, before=inf, cluster=0,
324 0f510652 Sofia Papagiannaki
                            update_statistics_ancestors_depth=None):
325 4f917833 Sofia Papagiannaki
        """Delete all versions with the specified
326 4f917833 Sofia Papagiannaki
           parent and cluster, and return
327 0a92ff85 Sofia Papagiannaki
           the hashes, the total size and the serials of versions deleted.
328 4f917833 Sofia Papagiannaki
           Clears out nodes with no remaining versions.
329 4f917833 Sofia Papagiannaki
        """
330 b43d44ad Sofia Papagiannaki
        #update statistics
331 b43d44ad Sofia Papagiannaki
        c1 = select([self.nodes.c.node],
332 2715ade4 Sofia Papagiannaki
                    self.nodes.c.parent == parent)
333 b43d44ad Sofia Papagiannaki
        where_clause = and_(self.versions.c.node.in_(c1),
334 62d938dc Sofia Papagiannaki
                            self.versions.c.cluster == cluster)
335 0a92ff85 Sofia Papagiannaki
        if before != inf:
336 0a92ff85 Sofia Papagiannaki
            where_clause = and_(where_clause,
337 0a92ff85 Sofia Papagiannaki
                                self.versions.c.mtime <= before)
338 18266c93 Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
339 18266c93 Sofia Papagiannaki
                    func.sum(self.versions.c.size)])
340 18266c93 Sofia Papagiannaki
        s = s.where(where_clause)
341 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
342 18266c93 Sofia Papagiannaki
        row = r.fetchone()
343 18266c93 Sofia Papagiannaki
        r.close()
344 18266c93 Sofia Papagiannaki
        if not row:
345 442bc80c Sofia Papagiannaki
            return (), 0, ()
346 0a92ff85 Sofia Papagiannaki
        nr, size = row[0], row[1] if row[1] else 0
347 4f917833 Sofia Papagiannaki
        mtime = time()
348 0a92ff85 Sofia Papagiannaki
        self.statistics_update(parent, -nr, -size, mtime, cluster)
349 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(parent, -nr, -size, mtime, cluster,
350 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
351 2715ade4 Sofia Papagiannaki
352 388ea25f Sofia Papagiannaki
        s = select([self.versions.c.hash, self.versions.c.serial])
353 18266c93 Sofia Papagiannaki
        s = s.where(where_clause)
354 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
355 388ea25f Sofia Papagiannaki
        hashes = []
356 388ea25f Sofia Papagiannaki
        serials = []
357 388ea25f Sofia Papagiannaki
        for row in r.fetchall():
358 388ea25f Sofia Papagiannaki
            hashes += [row[0]]
359 388ea25f Sofia Papagiannaki
            serials += [row[1]]
360 18266c93 Sofia Papagiannaki
        r.close()
361 2715ade4 Sofia Papagiannaki
362 b43d44ad Sofia Papagiannaki
        #delete versions
363 18266c93 Sofia Papagiannaki
        s = self.versions.delete().where(where_clause)
364 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
365 18266c93 Sofia Papagiannaki
        r.close()
366 2715ade4 Sofia Papagiannaki
367 18266c93 Sofia Papagiannaki
        #delete nodes
368 b43d44ad Sofia Papagiannaki
        s = select([self.nodes.c.node],
369 2715ade4 Sofia Papagiannaki
                   and_(self.nodes.c.parent == parent,
370 2715ade4 Sofia Papagiannaki
                        select([func.count(self.versions.c.serial)],
371 29148653 Sofia Papagiannaki
                               self.versions.c.node == self.nodes.c.node).
372 29148653 Sofia Papagiannaki
                        as_scalar() == 0))
373 70516d86 Sofia Papagiannaki
        rp = self.conn.execute(s)
374 29148653 Sofia Papagiannaki
        nodes = [row[0] for row in rp.fetchall()]
375 70516d86 Sofia Papagiannaki
        rp.close()
376 c53502b1 Sofia Papagiannaki
        if nodes:
377 c53502b1 Sofia Papagiannaki
            s = self.nodes.delete().where(self.nodes.c.node.in_(nodes))
378 c53502b1 Sofia Papagiannaki
            self.conn.execute(s).close()
379 2715ade4 Sofia Papagiannaki
380 388ea25f Sofia Papagiannaki
        return hashes, size, serials
381 2715ade4 Sofia Papagiannaki
382 0f510652 Sofia Papagiannaki
    def node_purge(self, node, before=inf, cluster=0,
383 0f510652 Sofia Papagiannaki
                   update_statistics_ancestors_depth=None):
384 4f917833 Sofia Papagiannaki
        """Delete all versions with the specified
385 4f917833 Sofia Papagiannaki
           node and cluster, and return
386 813e42e5 Antony Chazapis
           the hashes and size of versions deleted.
387 4f917833 Sofia Papagiannaki
           Clears out the node if it has no remaining versions.
388 4f917833 Sofia Papagiannaki
        """
389 2715ade4 Sofia Papagiannaki
390 b43d44ad Sofia Papagiannaki
        #update statistics
391 b43d44ad Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
392 b43d44ad Sofia Papagiannaki
                    func.sum(self.versions.c.size)])
393 b43d44ad Sofia Papagiannaki
        where_clause = and_(self.versions.c.node == node,
394 2715ade4 Sofia Papagiannaki
                            self.versions.c.cluster == cluster)
395 62d938dc Sofia Papagiannaki
        if before != inf:
396 0a92ff85 Sofia Papagiannaki
            where_clause = and_(where_clause,
397 0a92ff85 Sofia Papagiannaki
                                self.versions.c.mtime <= before)
398 0a92ff85 Sofia Papagiannaki
        s = s.where(where_clause)
399 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
400 b43d44ad Sofia Papagiannaki
        row = r.fetchone()
401 b43d44ad Sofia Papagiannaki
        nr, size = row[0], row[1]
402 b43d44ad Sofia Papagiannaki
        r.close()
403 4f917833 Sofia Papagiannaki
        if not nr:
404 442bc80c Sofia Papagiannaki
            return (), 0, ()
405 4f917833 Sofia Papagiannaki
        mtime = time()
406 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -nr, -size, mtime, cluster,
407 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
408 2715ade4 Sofia Papagiannaki
409 388ea25f Sofia Papagiannaki
        s = select([self.versions.c.hash, self.versions.c.serial])
410 b43d44ad Sofia Papagiannaki
        s = s.where(where_clause)
411 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
412 388ea25f Sofia Papagiannaki
        hashes = []
413 388ea25f Sofia Papagiannaki
        serials = []
414 388ea25f Sofia Papagiannaki
        for row in r.fetchall():
415 388ea25f Sofia Papagiannaki
            hashes += [row[0]]
416 388ea25f Sofia Papagiannaki
            serials += [row[1]]
417 9eb713e1 Sofia Papagiannaki
        r.close()
418 2715ade4 Sofia Papagiannaki
419 b43d44ad Sofia Papagiannaki
        #delete versions
420 b43d44ad Sofia Papagiannaki
        s = self.versions.delete().where(where_clause)
421 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
422 b43d44ad Sofia Papagiannaki
        r.close()
423 2715ade4 Sofia Papagiannaki
424 b43d44ad Sofia Papagiannaki
        #delete nodes
425 b43d44ad Sofia Papagiannaki
        s = select([self.nodes.c.node],
426 2715ade4 Sofia Papagiannaki
                   and_(self.nodes.c.node == node,
427 2715ade4 Sofia Papagiannaki
                        select([func.count(self.versions.c.serial)],
428 29148653 Sofia Papagiannaki
                               self.versions.c.node == self.nodes.c.node).
429 29148653 Sofia Papagiannaki
                        as_scalar() == 0))
430 29148653 Sofia Papagiannaki
        rp = self.conn.execute(s)
431 29148653 Sofia Papagiannaki
        nodes = [row[0] for row in rp.fetchall()]
432 0a92ff85 Sofia Papagiannaki
        rp.close()
433 c53502b1 Sofia Papagiannaki
        if nodes:
434 c53502b1 Sofia Papagiannaki
            s = self.nodes.delete().where(self.nodes.c.node.in_(nodes))
435 c53502b1 Sofia Papagiannaki
            self.conn.execute(s).close()
436 2715ade4 Sofia Papagiannaki
437 388ea25f Sofia Papagiannaki
        return hashes, size, serials
438 2715ade4 Sofia Papagiannaki
439 0f510652 Sofia Papagiannaki
    def node_remove(self, node, update_statistics_ancestors_depth=None):
440 4f917833 Sofia Papagiannaki
        """Remove the node specified.
441 4f917833 Sofia Papagiannaki
           Return false if the node has children or is not found.
442 4f917833 Sofia Papagiannaki
        """
443 2715ade4 Sofia Papagiannaki
444 4f917833 Sofia Papagiannaki
        if self.node_count_children(node):
445 4f917833 Sofia Papagiannaki
            return False
446 2715ade4 Sofia Papagiannaki
447 4f917833 Sofia Papagiannaki
        mtime = time()
448 b43d44ad Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
449 b43d44ad Sofia Papagiannaki
                    func.sum(self.versions.c.size),
450 b43d44ad Sofia Papagiannaki
                    self.versions.c.cluster])
451 b43d44ad Sofia Papagiannaki
        s = s.where(self.versions.c.node == node)
452 b43d44ad Sofia Papagiannaki
        s = s.group_by(self.versions.c.cluster)
453 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
454 b43d44ad Sofia Papagiannaki
        for population, size, cluster in r.fetchall():
455 2715ade4 Sofia Papagiannaki
            self.statistics_update_ancestors(
456 0f510652 Sofia Papagiannaki
                node, -population, -size, mtime, cluster,
457 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth)
458 b43d44ad Sofia Papagiannaki
        r.close()
459 2715ade4 Sofia Papagiannaki
460 b43d44ad Sofia Papagiannaki
        s = self.nodes.delete().where(self.nodes.c.node == node)
461 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
462 4f917833 Sofia Papagiannaki
        return True
463 2715ade4 Sofia Papagiannaki
464 d1e7d2b4 Sofia Papagiannaki
    def node_accounts(self, accounts=()):
465 d1e7d2b4 Sofia Papagiannaki
        s = select([self.nodes.c.path, self.nodes.c.node])
466 d1e7d2b4 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.node != 0,
467 d1e7d2b4 Sofia Papagiannaki
                         self.nodes.c.parent == 0))
468 d1e7d2b4 Sofia Papagiannaki
        if accounts:
469 d1e7d2b4 Sofia Papagiannaki
            s = s.where(self.nodes.c.path.in_(accounts))
470 d1e7d2b4 Sofia Papagiannaki
        r = self.conn.execute(s)
471 d1e7d2b4 Sofia Papagiannaki
        rows = r.fetchall()
472 d1e7d2b4 Sofia Papagiannaki
        r.close()
473 d1e7d2b4 Sofia Papagiannaki
        return rows
474 d1e7d2b4 Sofia Papagiannaki
475 ae6199c5 Sofia Papagiannaki
    def node_account_quotas(self):
476 ae6199c5 Sofia Papagiannaki
        s = select([self.nodes.c.path, self.policy.c.value])
477 ae6199c5 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.node != 0,
478 ae6199c5 Sofia Papagiannaki
                         self.nodes.c.parent == 0))
479 ae6199c5 Sofia Papagiannaki
        s = s.where(self.nodes.c.node == self.policy.c.node)
480 ae6199c5 Sofia Papagiannaki
        s = s.where(self.policy.c.key == 'quota')
481 ae6199c5 Sofia Papagiannaki
        r = self.conn.execute(s)
482 ae6199c5 Sofia Papagiannaki
        rows = r.fetchall()
483 ae6199c5 Sofia Papagiannaki
        r.close()
484 ae6199c5 Sofia Papagiannaki
        return dict(rows)
485 ae6199c5 Sofia Papagiannaki
486 2ff02341 Sofia Papagiannaki
    def node_account_usage(self, account=None, cluster=0):
487 2ff02341 Sofia Papagiannaki
        """Return usage for a specific account.
488 2ff02341 Sofia Papagiannaki

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

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

1032 4f917833 Sofia Papagiannaki
           The property tuple for a version is returned if all
1033 4f917833 Sofia Papagiannaki
           of these conditions are true:
1034 2715ade4 Sofia Papagiannaki

1035 4f917833 Sofia Papagiannaki
                a. parent matches
1036 2715ade4 Sofia Papagiannaki

1037 4f917833 Sofia Papagiannaki
                b. path > start
1038 2715ade4 Sofia Papagiannaki

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

1041 4f917833 Sofia Papagiannaki
                d. version is the max up to before
1042 2715ade4 Sofia Papagiannaki

1043 4f917833 Sofia Papagiannaki
                e. version is not in cluster
1044 2715ade4 Sofia Papagiannaki

1045 4f917833 Sofia Papagiannaki
                f. the path does not have the delimiter occuring
1046 4f917833 Sofia Papagiannaki
                   after the prefix, or ends with the delimiter
1047 2715ade4 Sofia Papagiannaki

1048 4f917833 Sofia Papagiannaki
                g. serial matches the attribute filter query.
1049 2715ade4 Sofia Papagiannaki

1050 4f917833 Sofia Papagiannaki
                   A filter query is a comma-separated list of
1051 4f917833 Sofia Papagiannaki
                   terms in one of these three forms:
1052 2715ade4 Sofia Papagiannaki

1053 4f917833 Sofia Papagiannaki
                   key
1054 4f917833 Sofia Papagiannaki
                       an attribute with this key must exist
1055 2715ade4 Sofia Papagiannaki

1056 4f917833 Sofia Papagiannaki
                   !key
1057 4f917833 Sofia Papagiannaki
                       an attribute with this key must not exist
1058 2715ade4 Sofia Papagiannaki

1059 4f917833 Sofia Papagiannaki
                   key ?op value
1060 4f917833 Sofia Papagiannaki
                       the attribute with this key satisfies the value
1061 4f917833 Sofia Papagiannaki
                       where ?op is one of ==, != <=, >=, <, >.
1062 2715ade4 Sofia Papagiannaki

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

1065 4f917833 Sofia Papagiannaki
           The list of common prefixes includes the prefixes
1066 4f917833 Sofia Papagiannaki
           matching up to the first delimiter after prefix,
1067 4f917833 Sofia Papagiannaki
           and are reported only once, as "virtual directories".
1068 4f917833 Sofia Papagiannaki
           The delimiter is included in the prefixes.
1069 2715ade4 Sofia Papagiannaki

1070 4f917833 Sofia Papagiannaki
           If arguments are None, then the corresponding matching rule
1071 4f917833 Sofia Papagiannaki
           will always match.
1072 2715ade4 Sofia Papagiannaki

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

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

1209 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1210 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1211 2bbf1544 Georgios D. Tsoukalas

1212 2bbf1544 Georgios D. Tsoukalas
        """
1213 2715ade4 Sofia Papagiannaki
1214 37bee317 Antony Chazapis
        v = self.versions.alias('v')
1215 37bee317 Antony Chazapis
        n = self.nodes.alias('n')
1216 37bee317 Antony Chazapis
        s = select([n.c.path, v.c.serial])
1217 37bee317 Antony Chazapis
        filtered = select([func.max(self.versions.c.serial)])
1218 2bbf1544 Georgios D. Tsoukalas
        filtered = filtered.where(self.versions.c.uuid == uuid)
1219 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
1220 2bbf1544 Georgios D. Tsoukalas
            filtered = filtered.where(self.versions.c.cluster == cluster)
1221 2bbf1544 Georgios D. Tsoukalas
        s = s.where(v.c.serial == filtered)
1222 37bee317 Antony Chazapis
        s = s.where(n.c.node == v.c.node)
1223 2715ade4 Sofia Papagiannaki
1224 37bee317 Antony Chazapis
        r = self.conn.execute(s)
1225 37bee317 Antony Chazapis
        l = r.fetchone()
1226 37bee317 Antony Chazapis
        r.close()
1227 37bee317 Antony Chazapis
        return l
1228 5576e6dd Sofia Papagiannaki
1229 8b365874 Sofia Papagiannaki
    def domain_object_list(self, domain, paths, cluster=None):
1230 5576e6dd Sofia Papagiannaki
        """Return a list of (path, property list, attribute dictionary)
1231 5576e6dd Sofia Papagiannaki
           for the objects in the specific domain and cluster.
1232 5576e6dd Sofia Papagiannaki
        """
1233 5576e6dd Sofia Papagiannaki
1234 5576e6dd Sofia Papagiannaki
        v = self.versions.alias('v')
1235 5576e6dd Sofia Papagiannaki
        n = self.nodes.alias('n')
1236 5576e6dd Sofia Papagiannaki
        a = self.attributes.alias('a')
1237 5576e6dd Sofia Papagiannaki
1238 5576e6dd Sofia Papagiannaki
        s = select([n.c.path, v.c.serial, v.c.node, v.c.hash, v.c.size,
1239 5576e6dd Sofia Papagiannaki
                    v.c.type, v.c.source, v.c.mtime, v.c.muser, v.c.uuid,
1240 5576e6dd Sofia Papagiannaki
                    v.c.checksum, v.c.cluster, a.c.key, a.c.value])
1241 5576e6dd Sofia Papagiannaki
        if cluster:
1242 5576e6dd Sofia Papagiannaki
            s = s.where(v.c.cluster == cluster)
1243 5576e6dd Sofia Papagiannaki
        s = s.where(v.c.serial == a.c.serial)
1244 5576e6dd Sofia Papagiannaki
        s = s.where(a.c.domain == domain)
1245 4100e0ee Sofia Papagiannaki
        s = s.where(a.c.node == n.c.node)
1246 fbafd482 Sofia Papagiannaki
        s = s.where(a.c.is_latest == True)
1247 8b365874 Sofia Papagiannaki
        if paths:
1248 8b365874 Sofia Papagiannaki
            s = s.where(n.c.path.in_(paths))
1249 5576e6dd Sofia Papagiannaki
1250 5576e6dd Sofia Papagiannaki
        r = self.conn.execute(s)
1251 5576e6dd Sofia Papagiannaki
        rows = r.fetchall()
1252 5576e6dd Sofia Papagiannaki
        r.close()
1253 5576e6dd Sofia Papagiannaki
1254 5576e6dd Sofia Papagiannaki
        group_by = itemgetter(slice(12))
1255 29148653 Sofia Papagiannaki
        rows.sort(key=group_by)
1256 5576e6dd Sofia Papagiannaki
        groups = groupby(rows, group_by)
1257 29148653 Sofia Papagiannaki
        return [(k[0], k[1:], dict([i[12:] for i in data])) for
1258 29148653 Sofia Papagiannaki
                (k, data) in groups]
1259 dc88754b Nanakos Chrysostomos
1260 dc88754b Nanakos Chrysostomos
    def get_props(self, paths):
1261 dc88754b Nanakos Chrysostomos
        inner_join = \
1262 dc88754b Nanakos Chrysostomos
            self.nodes.join(self.versions,
1263 7fef13f0 Nanakos Chrysostomos
                onclause=self.versions.c.serial == self.nodes.c.latest_version)
1264 dc88754b Nanakos Chrysostomos
        cc = self.nodes.c.path.in_(paths)
1265 7fef13f0 Nanakos Chrysostomos
        s = select([self.nodes.c.path, self.versions.c.type],
1266 dc88754b Nanakos Chrysostomos
                    from_obj=[inner_join]).where(cc).distinct()
1267 dc88754b Nanakos Chrysostomos
        r = self.conn.execute(s)
1268 dc88754b Nanakos Chrysostomos
        rows = r.fetchall()
1269 dc88754b Nanakos Chrysostomos
        r.close()
1270 dc88754b Nanakos Chrysostomos
        if rows:
1271 dc88754b Nanakos Chrysostomos
            return rows
1272 dc88754b Nanakos Chrysostomos
        return None