Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / lib / sqlalchemy / node.py @ 3759eddb

History | View | Annotate | Download (48.6 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 87835e94 Nanakos Chrysostomos
            c1 = select([func.max(self.versions.c.serial)],
654 87835e94 Nanakos Chrysostomos
                        and_(self.versions.c.mtime < before,
655 87835e94 Nanakos Chrysostomos
                             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 a3622324 Chrysostomos Nanakos
                       func.sum(v.c.size),
678 a3622324 Chrysostomos Nanakos
                       func.max(v.c.mtime)])
679 585b75e7 Sofia Papagiannaki
            c1 = select([func.max(self.versions.c.serial)],
680 87835e94 Nanakos Chrysostomos
                        and_(self.versions.c.mtime < before,
681 87835e94 Nanakos Chrysostomos
                             self.versions.c.node == v.c.node))
682 585b75e7 Sofia Papagiannaki
        else:
683 7fef13f0 Nanakos Chrysostomos
            inner_join = \
684 a3622324 Chrysostomos Nanakos
                self.versions.join(self.nodes, onclause=
685 a3622324 Chrysostomos Nanakos
                                   self.versions.c.serial ==
686 a3622324 Chrysostomos Nanakos
                                   self.nodes.c.latest_version)
687 7fef13f0 Nanakos Chrysostomos
            s = select([func.count(self.versions.c.serial),
688 a3622324 Chrysostomos Nanakos
                       func.sum(self.versions.c.size),
689 a3622324 Chrysostomos Nanakos
                       func.max(self.versions.c.mtime)],
690 a3622324 Chrysostomos Nanakos
                       from_obj=[inner_join])
691 7fef13f0 Nanakos Chrysostomos
692 96090728 Sofia Papagiannaki
        c2 = select([self.nodes.c.node],
693 96090728 Sofia Papagiannaki
                    self.nodes.c.path.like(self.escape_like(path) + '%',
694 96090728 Sofia Papagiannaki
                                           escape=ESCAPE_CHAR))
695 7fef13f0 Nanakos Chrysostomos
        if before != inf:
696 7fef13f0 Nanakos Chrysostomos
            s = s.where(and_(v.c.serial == c1,
697 a3622324 Chrysostomos Nanakos
                        v.c.cluster != except_cluster,
698 a3622324 Chrysostomos Nanakos
                        v.c.node.in_(c2)))
699 7fef13f0 Nanakos Chrysostomos
        else:
700 7fef13f0 Nanakos Chrysostomos
            s = s.where(and_(self.versions.c.cluster != except_cluster,
701 7fef13f0 Nanakos Chrysostomos
                        self.versions.c.node.in_(c2)))
702 7fef13f0 Nanakos Chrysostomos
703 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
704 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
705 b43d44ad Sofia Papagiannaki
        rp.close()
706 b43d44ad Sofia Papagiannaki
        if not r:
707 4f917833 Sofia Papagiannaki
            return None
708 87835e94 Nanakos Chrysostomos
        size = long(r[1] - props[SIZE])
709 4f917833 Sofia Papagiannaki
        mtime = max(mtime, r[2])
710 4f917833 Sofia Papagiannaki
        return (count, size, mtime)
711 2715ade4 Sofia Papagiannaki
712 585b75e7 Sofia Papagiannaki
    def nodes_set_latest_version(self, node, serial):
713 585b75e7 Sofia Papagiannaki
        s = self.nodes.update().where(self.nodes.c.node == node)
714 2715ade4 Sofia Papagiannaki
        s = s.values(latest_version=serial)
715 585b75e7 Sofia Papagiannaki
        self.conn.execute(s).close()
716 2715ade4 Sofia Papagiannaki
717 0f510652 Sofia Papagiannaki
    def version_create(self, node, hash, size, type, source, muser, uuid,
718 0f510652 Sofia Papagiannaki
                       checksum, cluster=0,
719 0f510652 Sofia Papagiannaki
                       update_statistics_ancestors_depth=None):
720 4f917833 Sofia Papagiannaki
        """Create a new version from the given properties.
721 4f917833 Sofia Papagiannaki
           Return the (serial, mtime) of the new version.
722 4f917833 Sofia Papagiannaki
        """
723 2715ade4 Sofia Papagiannaki
724 4f917833 Sofia Papagiannaki
        mtime = time()
725 29148653 Sofia Papagiannaki
        s = self.versions.insert().values(
726 29148653 Sofia Papagiannaki
            node=node, hash=hash, size=size, type=type, source=source,
727 29148653 Sofia Papagiannaki
            mtime=mtime, muser=muser, uuid=uuid, checksum=checksum,
728 29148653 Sofia Papagiannaki
            cluster=cluster)
729 b43d44ad Sofia Papagiannaki
        serial = self.conn.execute(s).inserted_primary_key[0]
730 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster,
731 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
732 2715ade4 Sofia Papagiannaki
733 585b75e7 Sofia Papagiannaki
        self.nodes_set_latest_version(node, serial)
734 2715ade4 Sofia Papagiannaki
735 4f917833 Sofia Papagiannaki
        return serial, mtime
736 2715ade4 Sofia Papagiannaki
737 8221c89d Sofia Papagiannaki
    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
738 4f917833 Sofia Papagiannaki
        """Lookup the current version of the given node.
739 4f917833 Sofia Papagiannaki
           Return a list with its properties:
740 2715ade4 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime,
741 2715ade4 Sofia Papagiannaki
            muser, uuid, checksum, cluster)
742 4f917833 Sofia Papagiannaki
           or None if the current version is not found in the given cluster.
743 4f917833 Sofia Papagiannaki
        """
744 2715ade4 Sofia Papagiannaki
745 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
746 8221c89d Sofia Papagiannaki
        if not all_props:
747 8221c89d Sofia Papagiannaki
            s = select([v.c.serial])
748 8221c89d Sofia Papagiannaki
        else:
749 8221c89d Sofia Papagiannaki
            s = select([v.c.serial, v.c.node, v.c.hash,
750 8221c89d Sofia Papagiannaki
                        v.c.size, v.c.type, v.c.source,
751 8221c89d Sofia Papagiannaki
                        v.c.mtime, v.c.muser, v.c.uuid,
752 8221c89d Sofia Papagiannaki
                        v.c.checksum, v.c.cluster])
753 62d938dc Sofia Papagiannaki
        if before != inf:
754 585b75e7 Sofia Papagiannaki
            c = select([func.max(self.versions.c.serial)],
755 2715ade4 Sofia Papagiannaki
                       self.versions.c.node == node)
756 62d938dc Sofia Papagiannaki
            c = c.where(self.versions.c.mtime < before)
757 585b75e7 Sofia Papagiannaki
        else:
758 585b75e7 Sofia Papagiannaki
            c = select([self.nodes.c.latest_version],
759 2715ade4 Sofia Papagiannaki
                       self.nodes.c.node == node)
760 b43d44ad Sofia Papagiannaki
        s = s.where(and_(v.c.serial == c,
761 b43d44ad Sofia Papagiannaki
                         v.c.cluster == cluster))
762 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
763 b43d44ad Sofia Papagiannaki
        props = r.fetchone()
764 b43d44ad Sofia Papagiannaki
        r.close()
765 70516d86 Sofia Papagiannaki
        if props:
766 4f917833 Sofia Papagiannaki
            return props
767 4f917833 Sofia Papagiannaki
        return None
768 2715ade4 Sofia Papagiannaki
769 29148653 Sofia Papagiannaki
    def version_lookup_bulk(self, nodes, before=inf, cluster=0,
770 ba402621 Sofia Papagiannaki
                            all_props=True, order_by_path=False):
771 8221c89d Sofia Papagiannaki
        """Lookup the current versions of the given nodes.
772 8221c89d Sofia Papagiannaki
           Return a list with their properties:
773 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
774 29148653 Sofia Papagiannaki
            checksum, cluster).
775 8221c89d Sofia Papagiannaki
        """
776 585b75e7 Sofia Papagiannaki
        if not nodes:
777 585b75e7 Sofia Papagiannaki
            return ()
778 8221c89d Sofia Papagiannaki
        v = self.versions.alias('v')
779 ba402621 Sofia Papagiannaki
        n = self.nodes.alias('n')
780 8221c89d Sofia Papagiannaki
        if not all_props:
781 8221c89d Sofia Papagiannaki
            s = select([v.c.serial])
782 8221c89d Sofia Papagiannaki
        else:
783 8221c89d Sofia Papagiannaki
            s = select([v.c.serial, v.c.node, v.c.hash,
784 8221c89d Sofia Papagiannaki
                        v.c.size, v.c.type, v.c.source,
785 8221c89d Sofia Papagiannaki
                        v.c.mtime, v.c.muser, v.c.uuid,
786 8221c89d Sofia Papagiannaki
                        v.c.checksum, v.c.cluster])
787 8221c89d Sofia Papagiannaki
        if before != inf:
788 585b75e7 Sofia Papagiannaki
            c = select([func.max(self.versions.c.serial)],
789 2715ade4 Sofia Papagiannaki
                       self.versions.c.node.in_(nodes))
790 8221c89d Sofia Papagiannaki
            c = c.where(self.versions.c.mtime < before)
791 585b75e7 Sofia Papagiannaki
            c = c.group_by(self.versions.c.node)
792 585b75e7 Sofia Papagiannaki
        else:
793 585b75e7 Sofia Papagiannaki
            c = select([self.nodes.c.latest_version],
794 2715ade4 Sofia Papagiannaki
                       self.nodes.c.node.in_(nodes))
795 8221c89d Sofia Papagiannaki
        s = s.where(and_(v.c.serial.in_(c),
796 8221c89d Sofia Papagiannaki
                         v.c.cluster == cluster))
797 ba402621 Sofia Papagiannaki
        if order_by_path:
798 ba402621 Sofia Papagiannaki
            s = s.where(v.c.node == n.c.node)
799 ba402621 Sofia Papagiannaki
            s = s.order_by(n.c.path)
800 ba402621 Sofia Papagiannaki
        else:
801 ba402621 Sofia Papagiannaki
            s = s.order_by(v.c.node)
802 8221c89d Sofia Papagiannaki
        r = self.conn.execute(s)
803 8221c89d Sofia Papagiannaki
        rproxy = r.fetchall()
804 8221c89d Sofia Papagiannaki
        r.close()
805 8221c89d Sofia Papagiannaki
        return (tuple(row.values()) for row in rproxy)
806 2715ade4 Sofia Papagiannaki
807 d2fc71c9 Sofia Papagiannaki
    def version_get_properties(self, serial, keys=(), propnames=_propnames,
808 d2fc71c9 Sofia Papagiannaki
                               node=None):
809 4f917833 Sofia Papagiannaki
        """Return a sequence of values for the properties of
810 4f917833 Sofia Papagiannaki
           the version specified by serial and the keys, in the order given.
811 4f917833 Sofia Papagiannaki
           If keys is empty, return all properties in the order
812 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
813 29148653 Sofia Papagiannaki
            checksum, cluster).
814 4f917833 Sofia Papagiannaki
        """
815 2715ade4 Sofia Papagiannaki
816 b43d44ad Sofia Papagiannaki
        v = self.versions.alias()
817 37bee317 Antony Chazapis
        s = select([v.c.serial, v.c.node, v.c.hash,
818 cf341da4 Antony Chazapis
                    v.c.size, v.c.type, v.c.source,
819 cf341da4 Antony Chazapis
                    v.c.mtime, v.c.muser, v.c.uuid,
820 f7d5b0cf Antony Chazapis
                    v.c.checksum, v.c.cluster], v.c.serial == serial)
821 d2fc71c9 Sofia Papagiannaki
        if node is not None:
822 d2fc71c9 Sofia Papagiannaki
            s = s.where(v.c.node == node)
823 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
824 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
825 b43d44ad Sofia Papagiannaki
        rp.close()
826 4f917833 Sofia Papagiannaki
        if r is None:
827 4f917833 Sofia Papagiannaki
            return r
828 2715ade4 Sofia Papagiannaki
829 4f917833 Sofia Papagiannaki
        if not keys:
830 4f917833 Sofia Papagiannaki
            return r
831 4f917833 Sofia Papagiannaki
        return [r[propnames[k]] for k in keys if k in propnames]
832 2715ade4 Sofia Papagiannaki
833 f7d5b0cf Antony Chazapis
    def version_put_property(self, serial, key, value):
834 f7d5b0cf Antony Chazapis
        """Set value for the property of version specified by key."""
835 2715ade4 Sofia Papagiannaki
836 f7d5b0cf Antony Chazapis
        if key not in _propnames:
837 f7d5b0cf Antony Chazapis
            return
838 f7d5b0cf Antony Chazapis
        s = self.versions.update()
839 f7d5b0cf Antony Chazapis
        s = s.where(self.versions.c.serial == serial)
840 f7d5b0cf Antony Chazapis
        s = s.values(**{key: value})
841 f7d5b0cf Antony Chazapis
        self.conn.execute(s).close()
842 2715ade4 Sofia Papagiannaki
843 0f510652 Sofia Papagiannaki
    def version_recluster(self, serial, cluster,
844 0f510652 Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
845 4f917833 Sofia Papagiannaki
        """Move the version into another cluster."""
846 2715ade4 Sofia Papagiannaki
847 4f917833 Sofia Papagiannaki
        props = self.version_get_properties(serial)
848 4f917833 Sofia Papagiannaki
        if not props:
849 4f917833 Sofia Papagiannaki
            return
850 4f917833 Sofia Papagiannaki
        node = props[NODE]
851 4f917833 Sofia Papagiannaki
        size = props[SIZE]
852 4f917833 Sofia Papagiannaki
        oldcluster = props[CLUSTER]
853 4f917833 Sofia Papagiannaki
        if cluster == oldcluster:
854 4f917833 Sofia Papagiannaki
            return
855 2715ade4 Sofia Papagiannaki
856 4f917833 Sofia Papagiannaki
        mtime = time()
857 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster,
858 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
859 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster,
860 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
861 2715ade4 Sofia Papagiannaki
862 b43d44ad Sofia Papagiannaki
        s = self.versions.update()
863 b43d44ad Sofia Papagiannaki
        s = s.where(self.versions.c.serial == serial)
864 2715ade4 Sofia Papagiannaki
        s = s.values(cluster=cluster)
865 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
866 2715ade4 Sofia Papagiannaki
867 0f510652 Sofia Papagiannaki
    def version_remove(self, serial, update_statistics_ancestors_depth=None):
868 4f917833 Sofia Papagiannaki
        """Remove the serial specified."""
869 2715ade4 Sofia Papagiannaki
870 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
871 4f917833 Sofia Papagiannaki
        if not props:
872 4f917833 Sofia Papagiannaki
            return
873 4f917833 Sofia Papagiannaki
        node = props[NODE]
874 5161c672 Antony Chazapis
        hash = props[HASH]
875 4f917833 Sofia Papagiannaki
        size = props[SIZE]
876 4f917833 Sofia Papagiannaki
        cluster = props[CLUSTER]
877 2715ade4 Sofia Papagiannaki
878 4f917833 Sofia Papagiannaki
        mtime = time()
879 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster,
880 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
881 2715ade4 Sofia Papagiannaki
882 b43d44ad Sofia Papagiannaki
        s = self.versions.delete().where(self.versions.c.serial == serial)
883 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
884 2715ade4 Sofia Papagiannaki
885 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
886 585b75e7 Sofia Papagiannaki
        if props:
887 eecad161 Sofia Papagiannaki
            self.nodes_set_latest_version(node, serial)
888 2715ade4 Sofia Papagiannaki
889 813e42e5 Antony Chazapis
        return hash, size
890 2715ade4 Sofia Papagiannaki
891 059857e2 Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
892 29148653 Sofia Papagiannaki
        """Return a list of (key, value) pairs of the specific version.
893 29148653 Sofia Papagiannaki

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

1038 4f917833 Sofia Papagiannaki
           The property tuple for a version is returned if all
1039 4f917833 Sofia Papagiannaki
           of these conditions are true:
1040 2715ade4 Sofia Papagiannaki

1041 4f917833 Sofia Papagiannaki
                a. parent matches
1042 2715ade4 Sofia Papagiannaki

1043 4f917833 Sofia Papagiannaki
                b. path > start
1044 2715ade4 Sofia Papagiannaki

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

1047 4f917833 Sofia Papagiannaki
                d. version is the max up to before
1048 2715ade4 Sofia Papagiannaki

1049 4f917833 Sofia Papagiannaki
                e. version is not in cluster
1050 2715ade4 Sofia Papagiannaki

1051 4f917833 Sofia Papagiannaki
                f. the path does not have the delimiter occuring
1052 4f917833 Sofia Papagiannaki
                   after the prefix, or ends with the delimiter
1053 2715ade4 Sofia Papagiannaki

1054 4f917833 Sofia Papagiannaki
                g. serial matches the attribute filter query.
1055 2715ade4 Sofia Papagiannaki

1056 4f917833 Sofia Papagiannaki
                   A filter query is a comma-separated list of
1057 4f917833 Sofia Papagiannaki
                   terms in one of these three forms:
1058 2715ade4 Sofia Papagiannaki

1059 4f917833 Sofia Papagiannaki
                   key
1060 4f917833 Sofia Papagiannaki
                       an attribute with this key must exist
1061 2715ade4 Sofia Papagiannaki

1062 4f917833 Sofia Papagiannaki
                   !key
1063 4f917833 Sofia Papagiannaki
                       an attribute with this key must not exist
1064 2715ade4 Sofia Papagiannaki

1065 4f917833 Sofia Papagiannaki
                   key ?op value
1066 4f917833 Sofia Papagiannaki
                       the attribute with this key satisfies the value
1067 4f917833 Sofia Papagiannaki
                       where ?op is one of ==, != <=, >=, <, >.
1068 2715ade4 Sofia Papagiannaki

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

1071 4f917833 Sofia Papagiannaki
           The list of common prefixes includes the prefixes
1072 4f917833 Sofia Papagiannaki
           matching up to the first delimiter after prefix,
1073 4f917833 Sofia Papagiannaki
           and are reported only once, as "virtual directories".
1074 4f917833 Sofia Papagiannaki
           The delimiter is included in the prefixes.
1075 2715ade4 Sofia Papagiannaki

1076 4f917833 Sofia Papagiannaki
           If arguments are None, then the corresponding matching rule
1077 4f917833 Sofia Papagiannaki
           will always match.
1078 2715ade4 Sofia Papagiannaki

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

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

1229 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1230 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1231 2bbf1544 Georgios D. Tsoukalas

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