Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / lib / sqlalchemy / node.py @ 2c2513fc

History | View | Annotate | Download (48 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 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 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 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 87835e94 Nanakos Chrysostomos
        size = long(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 2c2513fc Sofia Papagiannaki
                            all_props=True, order_by_path=False):
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 2c2513fc Sofia Papagiannaki
        n = self.nodes.alias('n')
778 8221c89d Sofia Papagiannaki
        if not all_props:
779 8221c89d Sofia Papagiannaki
            s = select([v.c.serial])
780 8221c89d Sofia Papagiannaki
        else:
781 8221c89d Sofia Papagiannaki
            s = select([v.c.serial, v.c.node, v.c.hash,
782 8221c89d Sofia Papagiannaki
                        v.c.size, v.c.type, v.c.source,
783 8221c89d Sofia Papagiannaki
                        v.c.mtime, v.c.muser, v.c.uuid,
784 8221c89d Sofia Papagiannaki
                        v.c.checksum, v.c.cluster])
785 8221c89d Sofia Papagiannaki
        if before != inf:
786 585b75e7 Sofia Papagiannaki
            c = select([func.max(self.versions.c.serial)],
787 2715ade4 Sofia Papagiannaki
                       self.versions.c.node.in_(nodes))
788 8221c89d Sofia Papagiannaki
            c = c.where(self.versions.c.mtime < before)
789 585b75e7 Sofia Papagiannaki
            c = c.group_by(self.versions.c.node)
790 585b75e7 Sofia Papagiannaki
        else:
791 585b75e7 Sofia Papagiannaki
            c = select([self.nodes.c.latest_version],
792 2715ade4 Sofia Papagiannaki
                       self.nodes.c.node.in_(nodes))
793 8221c89d Sofia Papagiannaki
        s = s.where(and_(v.c.serial.in_(c),
794 8221c89d Sofia Papagiannaki
                         v.c.cluster == cluster))
795 2c2513fc Sofia Papagiannaki
        if order_by_path:
796 2c2513fc Sofia Papagiannaki
            s = s.where(v.c.node == n.c.node)
797 2c2513fc Sofia Papagiannaki
            s = s.order_by(n.c.path)
798 2c2513fc Sofia Papagiannaki
        else:
799 2c2513fc Sofia Papagiannaki
            s = s.order_by(v.c.node)
800 8221c89d Sofia Papagiannaki
        r = self.conn.execute(s)
801 8221c89d Sofia Papagiannaki
        rproxy = r.fetchall()
802 8221c89d Sofia Papagiannaki
        r.close()
803 8221c89d Sofia Papagiannaki
        return (tuple(row.values()) for row in rproxy)
804 2715ade4 Sofia Papagiannaki
805 d2fc71c9 Sofia Papagiannaki
    def version_get_properties(self, serial, keys=(), propnames=_propnames,
806 d2fc71c9 Sofia Papagiannaki
                               node=None):
807 4f917833 Sofia Papagiannaki
        """Return a sequence of values for the properties of
808 4f917833 Sofia Papagiannaki
           the version specified by serial and the keys, in the order given.
809 4f917833 Sofia Papagiannaki
           If keys is empty, return all properties in the order
810 29148653 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid,
811 29148653 Sofia Papagiannaki
            checksum, cluster).
812 4f917833 Sofia Papagiannaki
        """
813 2715ade4 Sofia Papagiannaki
814 b43d44ad Sofia Papagiannaki
        v = self.versions.alias()
815 37bee317 Antony Chazapis
        s = select([v.c.serial, v.c.node, v.c.hash,
816 cf341da4 Antony Chazapis
                    v.c.size, v.c.type, v.c.source,
817 cf341da4 Antony Chazapis
                    v.c.mtime, v.c.muser, v.c.uuid,
818 f7d5b0cf Antony Chazapis
                    v.c.checksum, v.c.cluster], v.c.serial == serial)
819 d2fc71c9 Sofia Papagiannaki
        if node is not None:
820 d2fc71c9 Sofia Papagiannaki
            s = s.where(v.c.node == node)
821 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
822 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
823 b43d44ad Sofia Papagiannaki
        rp.close()
824 4f917833 Sofia Papagiannaki
        if r is None:
825 4f917833 Sofia Papagiannaki
            return r
826 2715ade4 Sofia Papagiannaki
827 4f917833 Sofia Papagiannaki
        if not keys:
828 4f917833 Sofia Papagiannaki
            return r
829 4f917833 Sofia Papagiannaki
        return [r[propnames[k]] for k in keys if k in propnames]
830 2715ade4 Sofia Papagiannaki
831 f7d5b0cf Antony Chazapis
    def version_put_property(self, serial, key, value):
832 f7d5b0cf Antony Chazapis
        """Set value for the property of version specified by key."""
833 2715ade4 Sofia Papagiannaki
834 f7d5b0cf Antony Chazapis
        if key not in _propnames:
835 f7d5b0cf Antony Chazapis
            return
836 f7d5b0cf Antony Chazapis
        s = self.versions.update()
837 f7d5b0cf Antony Chazapis
        s = s.where(self.versions.c.serial == serial)
838 f7d5b0cf Antony Chazapis
        s = s.values(**{key: value})
839 f7d5b0cf Antony Chazapis
        self.conn.execute(s).close()
840 2715ade4 Sofia Papagiannaki
841 0f510652 Sofia Papagiannaki
    def version_recluster(self, serial, cluster,
842 0f510652 Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
843 4f917833 Sofia Papagiannaki
        """Move the version into another cluster."""
844 2715ade4 Sofia Papagiannaki
845 4f917833 Sofia Papagiannaki
        props = self.version_get_properties(serial)
846 4f917833 Sofia Papagiannaki
        if not props:
847 4f917833 Sofia Papagiannaki
            return
848 4f917833 Sofia Papagiannaki
        node = props[NODE]
849 4f917833 Sofia Papagiannaki
        size = props[SIZE]
850 4f917833 Sofia Papagiannaki
        oldcluster = props[CLUSTER]
851 4f917833 Sofia Papagiannaki
        if cluster == oldcluster:
852 4f917833 Sofia Papagiannaki
            return
853 2715ade4 Sofia Papagiannaki
854 4f917833 Sofia Papagiannaki
        mtime = time()
855 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster,
856 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
857 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster,
858 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
859 2715ade4 Sofia Papagiannaki
860 b43d44ad Sofia Papagiannaki
        s = self.versions.update()
861 b43d44ad Sofia Papagiannaki
        s = s.where(self.versions.c.serial == serial)
862 2715ade4 Sofia Papagiannaki
        s = s.values(cluster=cluster)
863 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
864 2715ade4 Sofia Papagiannaki
865 0f510652 Sofia Papagiannaki
    def version_remove(self, serial, update_statistics_ancestors_depth=None):
866 4f917833 Sofia Papagiannaki
        """Remove the serial specified."""
867 2715ade4 Sofia Papagiannaki
868 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
869 4f917833 Sofia Papagiannaki
        if not props:
870 4f917833 Sofia Papagiannaki
            return
871 4f917833 Sofia Papagiannaki
        node = props[NODE]
872 5161c672 Antony Chazapis
        hash = props[HASH]
873 4f917833 Sofia Papagiannaki
        size = props[SIZE]
874 4f917833 Sofia Papagiannaki
        cluster = props[CLUSTER]
875 2715ade4 Sofia Papagiannaki
876 4f917833 Sofia Papagiannaki
        mtime = time()
877 0f510652 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster,
878 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth)
879 2715ade4 Sofia Papagiannaki
880 b43d44ad Sofia Papagiannaki
        s = self.versions.delete().where(self.versions.c.serial == serial)
881 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
882 2715ade4 Sofia Papagiannaki
883 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
884 585b75e7 Sofia Papagiannaki
        if props:
885 eecad161 Sofia Papagiannaki
            self.nodes_set_latest_version(node, serial)
886 2715ade4 Sofia Papagiannaki
887 813e42e5 Antony Chazapis
        return hash, size
888 2715ade4 Sofia Papagiannaki
889 059857e2 Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
890 29148653 Sofia Papagiannaki
        """Return a list of (key, value) pairs of the specific version.
891 29148653 Sofia Papagiannaki

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

1035 4f917833 Sofia Papagiannaki
           The property tuple for a version is returned if all
1036 4f917833 Sofia Papagiannaki
           of these conditions are true:
1037 2715ade4 Sofia Papagiannaki

1038 4f917833 Sofia Papagiannaki
                a. parent matches
1039 2715ade4 Sofia Papagiannaki

1040 4f917833 Sofia Papagiannaki
                b. path > start
1041 2715ade4 Sofia Papagiannaki

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

1044 4f917833 Sofia Papagiannaki
                d. version is the max up to before
1045 2715ade4 Sofia Papagiannaki

1046 4f917833 Sofia Papagiannaki
                e. version is not in cluster
1047 2715ade4 Sofia Papagiannaki

1048 4f917833 Sofia Papagiannaki
                f. the path does not have the delimiter occuring
1049 4f917833 Sofia Papagiannaki
                   after the prefix, or ends with the delimiter
1050 2715ade4 Sofia Papagiannaki

1051 4f917833 Sofia Papagiannaki
                g. serial matches the attribute filter query.
1052 2715ade4 Sofia Papagiannaki

1053 4f917833 Sofia Papagiannaki
                   A filter query is a comma-separated list of
1054 4f917833 Sofia Papagiannaki
                   terms in one of these three forms:
1055 2715ade4 Sofia Papagiannaki

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

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

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

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

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

1073 4f917833 Sofia Papagiannaki
           If arguments are None, then the corresponding matching rule
1074 4f917833 Sofia Papagiannaki
           will always match.
1075 2715ade4 Sofia Papagiannaki

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

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

1213 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1214 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1215 2bbf1544 Georgios D. Tsoukalas

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