Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (43.3 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 c48acbfd Sofia Papagiannaki
from sqlalchemy import Table, Integer, BigInteger, DECIMAL, Column, String, MetaData, ForeignKey
39 e414f54d Sofia Papagiannaki
from sqlalchemy.types import Text
40 18266c93 Sofia Papagiannaki
from sqlalchemy.schema import Index, Sequence
41 a847494c chazapis
from sqlalchemy.sql import func, and_, or_, not_, null, select, bindparam, text, exists
42 4f1bc0a6 Sofia Papagiannaki
from sqlalchemy.ext.compiler import compiles
43 e414f54d Sofia Papagiannaki
from sqlalchemy.engine.reflection import Inspector
44 6a82f89f Sofia Papagiannaki
from sqlalchemy.exc import NoSuchTableError
45 4f1bc0a6 Sofia Papagiannaki
46 4f917833 Sofia Papagiannaki
from dbworker import DBWorker
47 4f917833 Sofia Papagiannaki
48 6e147ecc Antony Chazapis
from pithos.backends.filter import parse_filters
49 9d41b050 chazapis
50 059857e2 Antony Chazapis
51 2715ade4 Sofia Papagiannaki
ROOTNODE = 0
52 4f917833 Sofia Papagiannaki
53 2715ade4 Sofia Papagiannaki
(SERIAL, NODE, HASH, SIZE, TYPE, SOURCE, MTIME, MUSER, UUID, CHECKSUM,
54 2715ade4 Sofia Papagiannaki
 CLUSTER) = range(11)
55 cf341da4 Antony Chazapis
56 2715ade4 Sofia Papagiannaki
(MATCH_PREFIX, MATCH_EXACT) = range(2)
57 4f917833 Sofia Papagiannaki
58 4f917833 Sofia Papagiannaki
inf = float('inf')
59 4f917833 Sofia Papagiannaki
60 4f917833 Sofia Papagiannaki
61 4f917833 Sofia Papagiannaki
def strnextling(prefix):
62 4f917833 Sofia Papagiannaki
    """Return the first unicode string
63 4f917833 Sofia Papagiannaki
       greater than but not starting with given prefix.
64 4f917833 Sofia Papagiannaki
       strnextling('hello') -> 'hellp'
65 4f917833 Sofia Papagiannaki
    """
66 4f917833 Sofia Papagiannaki
    if not prefix:
67 4f917833 Sofia Papagiannaki
        ## all strings start with the null string,
68 4f917833 Sofia Papagiannaki
        ## therefore we have to approximate strnextling('')
69 4f917833 Sofia Papagiannaki
        ## with the last unicode character supported by python
70 4f917833 Sofia Papagiannaki
        ## 0x10ffff for wide (32-bit unicode) python builds
71 4f917833 Sofia Papagiannaki
        ## 0x00ffff for narrow (16-bit unicode) python builds
72 4f917833 Sofia Papagiannaki
        ## We will not autodetect. 0xffff is safe enough.
73 4f917833 Sofia Papagiannaki
        return unichr(0xffff)
74 4f917833 Sofia Papagiannaki
    s = prefix[:-1]
75 4f917833 Sofia Papagiannaki
    c = ord(prefix[-1])
76 4f917833 Sofia Papagiannaki
    if c >= 0xffff:
77 4f917833 Sofia Papagiannaki
        raise RuntimeError
78 2715ade4 Sofia Papagiannaki
    s += unichr(c + 1)
79 4f917833 Sofia Papagiannaki
    return s
80 4f917833 Sofia Papagiannaki
81 2715ade4 Sofia Papagiannaki
82 4f917833 Sofia Papagiannaki
def strprevling(prefix):
83 4f917833 Sofia Papagiannaki
    """Return an approximation of the last unicode string
84 4f917833 Sofia Papagiannaki
       less than but not starting with given prefix.
85 4f917833 Sofia Papagiannaki
       strprevling(u'hello') -> u'helln\\xffff'
86 4f917833 Sofia Papagiannaki
    """
87 4f917833 Sofia Papagiannaki
    if not prefix:
88 4f917833 Sofia Papagiannaki
        ## There is no prevling for the null string
89 4f917833 Sofia Papagiannaki
        return prefix
90 4f917833 Sofia Papagiannaki
    s = prefix[:-1]
91 4f917833 Sofia Papagiannaki
    c = ord(prefix[-1])
92 4f917833 Sofia Papagiannaki
    if c > 0:
93 2715ade4 Sofia Papagiannaki
        s += unichr(c - 1) + unichr(0xffff)
94 4f917833 Sofia Papagiannaki
    return s
95 4f917833 Sofia Papagiannaki
96 4f917833 Sofia Papagiannaki
_propnames = {
97 2715ade4 Sofia Papagiannaki
    'serial': 0,
98 2715ade4 Sofia Papagiannaki
    'node': 1,
99 2715ade4 Sofia Papagiannaki
    'hash': 2,
100 2715ade4 Sofia Papagiannaki
    'size': 3,
101 2715ade4 Sofia Papagiannaki
    'type': 4,
102 2715ade4 Sofia Papagiannaki
    'source': 5,
103 2715ade4 Sofia Papagiannaki
    'mtime': 6,
104 2715ade4 Sofia Papagiannaki
    'muser': 7,
105 2715ade4 Sofia Papagiannaki
    'uuid': 8,
106 2715ade4 Sofia Papagiannaki
    'checksum': 9,
107 2715ade4 Sofia Papagiannaki
    'cluster': 10
108 4f917833 Sofia Papagiannaki
}
109 4f917833 Sofia Papagiannaki
110 2715ade4 Sofia Papagiannaki
111 6a82f89f Sofia Papagiannaki
def create_tables(engine):
112 6a82f89f Sofia Papagiannaki
    metadata = MetaData()
113 2715ade4 Sofia Papagiannaki
114 6a82f89f Sofia Papagiannaki
    #create nodes table
115 2715ade4 Sofia Papagiannaki
    columns = []
116 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer, primary_key=True))
117 6a82f89f Sofia Papagiannaki
    columns.append(Column('parent', Integer,
118 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
119 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
120 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
121 6a82f89f Sofia Papagiannaki
                          autoincrement=False))
122 6a82f89f Sofia Papagiannaki
    columns.append(Column('latest_version', Integer))
123 6a82f89f Sofia Papagiannaki
    columns.append(Column('path', String(2048), default='', nullable=False))
124 6a82f89f Sofia Papagiannaki
    nodes = Table('nodes', metadata, *columns, mysql_engine='InnoDB')
125 6a82f89f Sofia Papagiannaki
    Index('idx_nodes_path', nodes.c.path, unique=True)
126 6a82f89f Sofia Papagiannaki
    Index('idx_nodes_parent', nodes.c.parent)
127 2715ade4 Sofia Papagiannaki
128 6a82f89f Sofia Papagiannaki
    #create policy table
129 2715ade4 Sofia Papagiannaki
    columns = []
130 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
131 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
132 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
133 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
134 6a82f89f Sofia Papagiannaki
                          primary_key=True))
135 6a82f89f Sofia Papagiannaki
    columns.append(Column('key', String(128), primary_key=True))
136 6a82f89f Sofia Papagiannaki
    columns.append(Column('value', String(256)))
137 6a82f89f Sofia Papagiannaki
    policy = Table('policy', metadata, *columns, mysql_engine='InnoDB')
138 2715ade4 Sofia Papagiannaki
139 6a82f89f Sofia Papagiannaki
    #create statistics table
140 2715ade4 Sofia Papagiannaki
    columns = []
141 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
142 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
143 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
144 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
145 6a82f89f Sofia Papagiannaki
                          primary_key=True))
146 6a82f89f Sofia Papagiannaki
    columns.append(Column('population', Integer, nullable=False, default=0))
147 6a82f89f Sofia Papagiannaki
    columns.append(Column('size', BigInteger, nullable=False, default=0))
148 6a82f89f Sofia Papagiannaki
    columns.append(Column('mtime', DECIMAL(precision=16, scale=6)))
149 6a82f89f Sofia Papagiannaki
    columns.append(Column('cluster', Integer, nullable=False, default=0,
150 6a82f89f Sofia Papagiannaki
                          primary_key=True, autoincrement=False))
151 6a82f89f Sofia Papagiannaki
    statistics = Table('statistics', metadata, *columns, mysql_engine='InnoDB')
152 2715ade4 Sofia Papagiannaki
153 6a82f89f Sofia Papagiannaki
    #create versions table
154 2715ade4 Sofia Papagiannaki
    columns = []
155 6a82f89f Sofia Papagiannaki
    columns.append(Column('serial', Integer, primary_key=True))
156 6a82f89f Sofia Papagiannaki
    columns.append(Column('node', Integer,
157 6a82f89f Sofia Papagiannaki
                          ForeignKey('nodes.node',
158 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
159 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE')))
160 6a82f89f Sofia Papagiannaki
    columns.append(Column('hash', String(256)))
161 6a82f89f Sofia Papagiannaki
    columns.append(Column('size', BigInteger, nullable=False, default=0))
162 6a82f89f Sofia Papagiannaki
    columns.append(Column('type', String(256), nullable=False, default=''))
163 6a82f89f Sofia Papagiannaki
    columns.append(Column('source', Integer))
164 6a82f89f Sofia Papagiannaki
    columns.append(Column('mtime', DECIMAL(precision=16, scale=6)))
165 6a82f89f Sofia Papagiannaki
    columns.append(Column('muser', String(256), nullable=False, default=''))
166 6a82f89f Sofia Papagiannaki
    columns.append(Column('uuid', String(64), nullable=False, default=''))
167 6a82f89f Sofia Papagiannaki
    columns.append(Column('checksum', String(256), nullable=False, default=''))
168 6a82f89f Sofia Papagiannaki
    columns.append(Column('cluster', Integer, nullable=False, default=0))
169 6a82f89f Sofia Papagiannaki
    versions = Table('versions', metadata, *columns, mysql_engine='InnoDB')
170 6a82f89f Sofia Papagiannaki
    Index('idx_versions_node_mtime', versions.c.node, versions.c.mtime)
171 6a82f89f Sofia Papagiannaki
    Index('idx_versions_node_uuid', versions.c.uuid)
172 2715ade4 Sofia Papagiannaki
173 6a82f89f Sofia Papagiannaki
    #create attributes table
174 6a82f89f Sofia Papagiannaki
    columns = []
175 6a82f89f Sofia Papagiannaki
    columns.append(Column('serial', Integer,
176 6a82f89f Sofia Papagiannaki
                          ForeignKey('versions.serial',
177 6a82f89f Sofia Papagiannaki
                                     ondelete='CASCADE',
178 6a82f89f Sofia Papagiannaki
                                     onupdate='CASCADE'),
179 6a82f89f Sofia Papagiannaki
                          primary_key=True))
180 6a82f89f Sofia Papagiannaki
    columns.append(Column('domain', String(256), primary_key=True))
181 6a82f89f Sofia Papagiannaki
    columns.append(Column('key', String(128), primary_key=True))
182 6a82f89f Sofia Papagiannaki
    columns.append(Column('value', String(256)))
183 6a82f89f Sofia Papagiannaki
    attributes = Table('attributes', metadata, *columns, mysql_engine='InnoDB')
184 5576e6dd Sofia Papagiannaki
    Index('idx_attributes_domain', attributes.c.domain)
185 2715ade4 Sofia Papagiannaki
186 6a82f89f Sofia Papagiannaki
    metadata.create_all(engine)
187 6a82f89f Sofia Papagiannaki
    return metadata.sorted_tables
188 4f917833 Sofia Papagiannaki
189 2715ade4 Sofia Papagiannaki
190 4f917833 Sofia Papagiannaki
class Node(DBWorker):
191 4f917833 Sofia Papagiannaki
    """Nodes store path organization and have multiple versions.
192 4f917833 Sofia Papagiannaki
       Versions store object history and have multiple attributes.
193 4f917833 Sofia Papagiannaki
       Attributes store metadata.
194 4f917833 Sofia Papagiannaki
    """
195 2715ade4 Sofia Papagiannaki
196 4f917833 Sofia Papagiannaki
    # TODO: Provide an interface for included and excluded clusters.
197 2715ade4 Sofia Papagiannaki
198 4f917833 Sofia Papagiannaki
    def __init__(self, **params):
199 4f917833 Sofia Papagiannaki
        DBWorker.__init__(self, **params)
200 6a82f89f Sofia Papagiannaki
        try:
201 6a82f89f Sofia Papagiannaki
            metadata = MetaData(self.engine)
202 6a82f89f Sofia Papagiannaki
            self.nodes = Table('nodes', metadata, autoload=True)
203 6a82f89f Sofia Papagiannaki
            self.policy = Table('policy', metadata, autoload=True)
204 6a82f89f Sofia Papagiannaki
            self.statistics = Table('statistics', metadata, autoload=True)
205 6a82f89f Sofia Papagiannaki
            self.versions = Table('versions', metadata, autoload=True)
206 6a82f89f Sofia Papagiannaki
            self.attributes = Table('attributes', metadata, autoload=True)
207 6a82f89f Sofia Papagiannaki
        except NoSuchTableError:
208 6a82f89f Sofia Papagiannaki
            tables = create_tables(self.engine)
209 6a82f89f Sofia Papagiannaki
            map(lambda t: self.__setattr__(t.name, t), tables)
210 2715ade4 Sofia Papagiannaki
211 d3be972a Sofia Papagiannaki
        s = self.nodes.select().where(and_(self.nodes.c.node == ROOTNODE,
212 d3be972a Sofia Papagiannaki
                                           self.nodes.c.parent == ROOTNODE))
213 26837206 Georgios D. Tsoukalas
        wrapper = self.wrapper
214 26837206 Georgios D. Tsoukalas
        wrapper.execute()
215 26837206 Georgios D. Tsoukalas
        try:
216 26837206 Georgios D. Tsoukalas
            rp = self.conn.execute(s)
217 26837206 Georgios D. Tsoukalas
            r = rp.fetchone()
218 26837206 Georgios D. Tsoukalas
            rp.close()
219 26837206 Georgios D. Tsoukalas
            if not r:
220 26837206 Georgios D. Tsoukalas
                s = self.nodes.insert(
221 26837206 Georgios D. Tsoukalas
                ).values(node=ROOTNODE, parent=ROOTNODE, path='')
222 26837206 Georgios D. Tsoukalas
                self.conn.execute(s)
223 26837206 Georgios D. Tsoukalas
        finally:
224 26837206 Georgios D. Tsoukalas
            wrapper.commit()
225 2715ade4 Sofia Papagiannaki
226 4f917833 Sofia Papagiannaki
    def node_create(self, parent, path):
227 4f917833 Sofia Papagiannaki
        """Create a new node from the given properties.
228 4f917833 Sofia Papagiannaki
           Return the node identifier of the new node.
229 4f917833 Sofia Papagiannaki
        """
230 18266c93 Sofia Papagiannaki
        #TODO catch IntegrityError?
231 18266c93 Sofia Papagiannaki
        s = self.nodes.insert().values(parent=parent, path=path)
232 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
233 18266c93 Sofia Papagiannaki
        inserted_primary_key = r.inserted_primary_key[0]
234 18266c93 Sofia Papagiannaki
        r.close()
235 18266c93 Sofia Papagiannaki
        return inserted_primary_key
236 2715ade4 Sofia Papagiannaki
237 4f917833 Sofia Papagiannaki
    def node_lookup(self, path):
238 4f917833 Sofia Papagiannaki
        """Lookup the current node of the given path.
239 4f917833 Sofia Papagiannaki
           Return None if the path is not found.
240 4f917833 Sofia Papagiannaki
        """
241 2715ade4 Sofia Papagiannaki
242 6b20cfbc Antony Chazapis
        # Use LIKE for comparison to avoid MySQL problems with trailing spaces.
243 2715ade4 Sofia Papagiannaki
        s = select([self.nodes.c.node], self.nodes.c.path.like(
244 2715ade4 Sofia Papagiannaki
            self.escape_like(path), escape='\\'))
245 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
246 18266c93 Sofia Papagiannaki
        row = r.fetchone()
247 18266c93 Sofia Papagiannaki
        r.close()
248 18266c93 Sofia Papagiannaki
        if row:
249 18266c93 Sofia Papagiannaki
            return row[0]
250 4f917833 Sofia Papagiannaki
        return None
251 2715ade4 Sofia Papagiannaki
252 8221c89d Sofia Papagiannaki
    def node_lookup_bulk(self, paths):
253 8221c89d Sofia Papagiannaki
        """Lookup the current nodes for the given paths.
254 8221c89d Sofia Papagiannaki
           Return () if the path is not found.
255 8221c89d Sofia Papagiannaki
        """
256 2715ade4 Sofia Papagiannaki
257 c53502b1 Sofia Papagiannaki
        if not paths:
258 c53502b1 Sofia Papagiannaki
            return ()
259 8221c89d Sofia Papagiannaki
        # Use LIKE for comparison to avoid MySQL problems with trailing spaces.
260 8221c89d Sofia Papagiannaki
        s = select([self.nodes.c.node], self.nodes.c.path.in_(paths))
261 8221c89d Sofia Papagiannaki
        r = self.conn.execute(s)
262 8221c89d Sofia Papagiannaki
        rows = r.fetchall()
263 8221c89d Sofia Papagiannaki
        r.close()
264 8221c89d Sofia Papagiannaki
        return [row[0] for row in rows]
265 2715ade4 Sofia Papagiannaki
266 4f917833 Sofia Papagiannaki
    def node_get_properties(self, node):
267 4f917833 Sofia Papagiannaki
        """Return the node's (parent, path).
268 4f917833 Sofia Papagiannaki
           Return None if the node is not found.
269 4f917833 Sofia Papagiannaki
        """
270 2715ade4 Sofia Papagiannaki
271 18266c93 Sofia Papagiannaki
        s = select([self.nodes.c.parent, self.nodes.c.path])
272 18266c93 Sofia Papagiannaki
        s = s.where(self.nodes.c.node == node)
273 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
274 18266c93 Sofia Papagiannaki
        l = r.fetchone()
275 18266c93 Sofia Papagiannaki
        r.close()
276 18266c93 Sofia Papagiannaki
        return l
277 2715ade4 Sofia Papagiannaki
278 4f917833 Sofia Papagiannaki
    def node_get_versions(self, node, keys=(), propnames=_propnames):
279 4f917833 Sofia Papagiannaki
        """Return the properties of all versions at node.
280 4f917833 Sofia Papagiannaki
           If keys is empty, return all properties in the order
281 f7d5b0cf Antony Chazapis
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
282 4f917833 Sofia Papagiannaki
        """
283 2715ade4 Sofia Papagiannaki
284 1c2fc0ff Antony Chazapis
        s = select([self.versions.c.serial,
285 1c2fc0ff Antony Chazapis
                    self.versions.c.node,
286 1c2fc0ff Antony Chazapis
                    self.versions.c.hash,
287 1c2fc0ff Antony Chazapis
                    self.versions.c.size,
288 cf341da4 Antony Chazapis
                    self.versions.c.type,
289 1c2fc0ff Antony Chazapis
                    self.versions.c.source,
290 1c2fc0ff Antony Chazapis
                    self.versions.c.mtime,
291 1c2fc0ff Antony Chazapis
                    self.versions.c.muser,
292 25ae8b75 Antony Chazapis
                    self.versions.c.uuid,
293 f7d5b0cf Antony Chazapis
                    self.versions.c.checksum,
294 1c2fc0ff Antony Chazapis
                    self.versions.c.cluster], self.versions.c.node == node)
295 cc28f894 Sofia Papagiannaki
        s = s.order_by(self.versions.c.serial)
296 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
297 18266c93 Sofia Papagiannaki
        rows = r.fetchall()
298 9eb713e1 Sofia Papagiannaki
        r.close()
299 18266c93 Sofia Papagiannaki
        if not rows:
300 18266c93 Sofia Papagiannaki
            return rows
301 2715ade4 Sofia Papagiannaki
302 4f917833 Sofia Papagiannaki
        if not keys:
303 18266c93 Sofia Papagiannaki
            return rows
304 2715ade4 Sofia Papagiannaki
305 18266c93 Sofia Papagiannaki
        return [[p[propnames[k]] for k in keys if k in propnames] for p in rows]
306 2715ade4 Sofia Papagiannaki
307 4f917833 Sofia Papagiannaki
    def node_count_children(self, node):
308 4f917833 Sofia Papagiannaki
        """Return node's child count."""
309 2715ade4 Sofia Papagiannaki
310 18266c93 Sofia Papagiannaki
        s = select([func.count(self.nodes.c.node)])
311 18266c93 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.parent == node,
312 18266c93 Sofia Papagiannaki
                         self.nodes.c.node != ROOTNODE))
313 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
314 18266c93 Sofia Papagiannaki
        row = r.fetchone()
315 18266c93 Sofia Papagiannaki
        r.close()
316 18266c93 Sofia Papagiannaki
        return row[0]
317 2715ade4 Sofia Papagiannaki
318 4f917833 Sofia Papagiannaki
    def node_purge_children(self, parent, before=inf, cluster=0):
319 4f917833 Sofia Papagiannaki
        """Delete all versions with the specified
320 4f917833 Sofia Papagiannaki
           parent and cluster, and return
321 0a92ff85 Sofia Papagiannaki
           the hashes, the total size and the serials of versions deleted.
322 4f917833 Sofia Papagiannaki
           Clears out nodes with no remaining versions.
323 4f917833 Sofia Papagiannaki
        """
324 b43d44ad Sofia Papagiannaki
        #update statistics
325 b43d44ad Sofia Papagiannaki
        c1 = select([self.nodes.c.node],
326 2715ade4 Sofia Papagiannaki
                    self.nodes.c.parent == parent)
327 b43d44ad Sofia Papagiannaki
        where_clause = and_(self.versions.c.node.in_(c1),
328 62d938dc Sofia Papagiannaki
                            self.versions.c.cluster == cluster)
329 0a92ff85 Sofia Papagiannaki
        if before != inf:
330 0a92ff85 Sofia Papagiannaki
            where_clause = and_(where_clause,
331 0a92ff85 Sofia Papagiannaki
                                self.versions.c.mtime <= before)
332 18266c93 Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
333 18266c93 Sofia Papagiannaki
                    func.sum(self.versions.c.size)])
334 18266c93 Sofia Papagiannaki
        s = s.where(where_clause)
335 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
336 18266c93 Sofia Papagiannaki
        row = r.fetchone()
337 18266c93 Sofia Papagiannaki
        r.close()
338 18266c93 Sofia Papagiannaki
        if not row:
339 442bc80c Sofia Papagiannaki
            return (), 0, ()
340 0a92ff85 Sofia Papagiannaki
        nr, size = row[0], row[1] if row[1] else 0
341 4f917833 Sofia Papagiannaki
        mtime = time()
342 0a92ff85 Sofia Papagiannaki
        self.statistics_update(parent, -nr, -size, mtime, cluster)
343 0a92ff85 Sofia Papagiannaki
        self.statistics_update_ancestors(parent, -nr, -size, mtime, cluster)
344 2715ade4 Sofia Papagiannaki
345 388ea25f Sofia Papagiannaki
        s = select([self.versions.c.hash, self.versions.c.serial])
346 18266c93 Sofia Papagiannaki
        s = s.where(where_clause)
347 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
348 388ea25f Sofia Papagiannaki
        hashes = []
349 388ea25f Sofia Papagiannaki
        serials = []
350 388ea25f Sofia Papagiannaki
        for row in r.fetchall():
351 388ea25f Sofia Papagiannaki
            hashes += [row[0]]
352 388ea25f Sofia Papagiannaki
            serials += [row[1]]
353 18266c93 Sofia Papagiannaki
        r.close()
354 2715ade4 Sofia Papagiannaki
355 b43d44ad Sofia Papagiannaki
        #delete versions
356 18266c93 Sofia Papagiannaki
        s = self.versions.delete().where(where_clause)
357 18266c93 Sofia Papagiannaki
        r = self.conn.execute(s)
358 18266c93 Sofia Papagiannaki
        r.close()
359 2715ade4 Sofia Papagiannaki
360 18266c93 Sofia Papagiannaki
        #delete nodes
361 b43d44ad Sofia Papagiannaki
        s = select([self.nodes.c.node],
362 2715ade4 Sofia Papagiannaki
                   and_(self.nodes.c.parent == parent,
363 2715ade4 Sofia Papagiannaki
                        select([func.count(self.versions.c.serial)],
364 2715ade4 Sofia Papagiannaki
                               self.versions.c.node == self.nodes.c.node).as_scalar() == 0))
365 70516d86 Sofia Papagiannaki
        rp = self.conn.execute(s)
366 70516d86 Sofia Papagiannaki
        nodes = [r[0] for r in rp.fetchall()]
367 70516d86 Sofia Papagiannaki
        rp.close()
368 c53502b1 Sofia Papagiannaki
        if nodes:
369 c53502b1 Sofia Papagiannaki
            s = self.nodes.delete().where(self.nodes.c.node.in_(nodes))
370 c53502b1 Sofia Papagiannaki
            self.conn.execute(s).close()
371 2715ade4 Sofia Papagiannaki
372 388ea25f Sofia Papagiannaki
        return hashes, size, serials
373 2715ade4 Sofia Papagiannaki
374 4f917833 Sofia Papagiannaki
    def node_purge(self, node, before=inf, cluster=0):
375 4f917833 Sofia Papagiannaki
        """Delete all versions with the specified
376 4f917833 Sofia Papagiannaki
           node and cluster, and return
377 813e42e5 Antony Chazapis
           the hashes and size of versions deleted.
378 4f917833 Sofia Papagiannaki
           Clears out the node if it has no remaining versions.
379 4f917833 Sofia Papagiannaki
        """
380 2715ade4 Sofia Papagiannaki
381 b43d44ad Sofia Papagiannaki
        #update statistics
382 b43d44ad Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
383 b43d44ad Sofia Papagiannaki
                    func.sum(self.versions.c.size)])
384 b43d44ad Sofia Papagiannaki
        where_clause = and_(self.versions.c.node == node,
385 2715ade4 Sofia Papagiannaki
                            self.versions.c.cluster == cluster)
386 62d938dc Sofia Papagiannaki
        if before != inf:
387 0a92ff85 Sofia Papagiannaki
            where_clause = and_(where_clause,
388 0a92ff85 Sofia Papagiannaki
                                self.versions.c.mtime <= before)
389 0a92ff85 Sofia Papagiannaki
        s = s.where(where_clause)
390 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
391 b43d44ad Sofia Papagiannaki
        row = r.fetchone()
392 b43d44ad Sofia Papagiannaki
        nr, size = row[0], row[1]
393 b43d44ad Sofia Papagiannaki
        r.close()
394 4f917833 Sofia Papagiannaki
        if not nr:
395 442bc80c Sofia Papagiannaki
            return (), 0, ()
396 4f917833 Sofia Papagiannaki
        mtime = time()
397 4f917833 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -nr, -size, mtime, cluster)
398 2715ade4 Sofia Papagiannaki
399 388ea25f Sofia Papagiannaki
        s = select([self.versions.c.hash, self.versions.c.serial])
400 b43d44ad Sofia Papagiannaki
        s = s.where(where_clause)
401 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
402 388ea25f Sofia Papagiannaki
        hashes = []
403 388ea25f Sofia Papagiannaki
        serials = []
404 388ea25f Sofia Papagiannaki
        for row in r.fetchall():
405 388ea25f Sofia Papagiannaki
            hashes += [row[0]]
406 388ea25f Sofia Papagiannaki
            serials += [row[1]]
407 9eb713e1 Sofia Papagiannaki
        r.close()
408 2715ade4 Sofia Papagiannaki
409 b43d44ad Sofia Papagiannaki
        #delete versions
410 b43d44ad Sofia Papagiannaki
        s = self.versions.delete().where(where_clause)
411 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
412 b43d44ad Sofia Papagiannaki
        r.close()
413 2715ade4 Sofia Papagiannaki
414 b43d44ad Sofia Papagiannaki
        #delete nodes
415 b43d44ad Sofia Papagiannaki
        s = select([self.nodes.c.node],
416 2715ade4 Sofia Papagiannaki
                   and_(self.nodes.c.node == node,
417 2715ade4 Sofia Papagiannaki
                        select([func.count(self.versions.c.serial)],
418 2715ade4 Sofia Papagiannaki
                               self.versions.c.node == self.nodes.c.node).as_scalar() == 0))
419 0a92ff85 Sofia Papagiannaki
        rp= self.conn.execute(s)
420 0a92ff85 Sofia Papagiannaki
        nodes = [r[0] for r in rp.fetchall()]
421 0a92ff85 Sofia Papagiannaki
        rp.close()
422 c53502b1 Sofia Papagiannaki
        if nodes:
423 c53502b1 Sofia Papagiannaki
            s = self.nodes.delete().where(self.nodes.c.node.in_(nodes))
424 c53502b1 Sofia Papagiannaki
            self.conn.execute(s).close()
425 2715ade4 Sofia Papagiannaki
426 388ea25f Sofia Papagiannaki
        return hashes, size, serials
427 2715ade4 Sofia Papagiannaki
428 4f917833 Sofia Papagiannaki
    def node_remove(self, node):
429 4f917833 Sofia Papagiannaki
        """Remove the node specified.
430 4f917833 Sofia Papagiannaki
           Return false if the node has children or is not found.
431 4f917833 Sofia Papagiannaki
        """
432 2715ade4 Sofia Papagiannaki
433 4f917833 Sofia Papagiannaki
        if self.node_count_children(node):
434 4f917833 Sofia Papagiannaki
            return False
435 2715ade4 Sofia Papagiannaki
436 4f917833 Sofia Papagiannaki
        mtime = time()
437 b43d44ad Sofia Papagiannaki
        s = select([func.count(self.versions.c.serial),
438 b43d44ad Sofia Papagiannaki
                    func.sum(self.versions.c.size),
439 b43d44ad Sofia Papagiannaki
                    self.versions.c.cluster])
440 b43d44ad Sofia Papagiannaki
        s = s.where(self.versions.c.node == node)
441 b43d44ad Sofia Papagiannaki
        s = s.group_by(self.versions.c.cluster)
442 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
443 b43d44ad Sofia Papagiannaki
        for population, size, cluster in r.fetchall():
444 2715ade4 Sofia Papagiannaki
            self.statistics_update_ancestors(
445 2715ade4 Sofia Papagiannaki
                node, -population, -size, mtime, cluster)
446 b43d44ad Sofia Papagiannaki
        r.close()
447 2715ade4 Sofia Papagiannaki
448 b43d44ad Sofia Papagiannaki
        s = self.nodes.delete().where(self.nodes.c.node == node)
449 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
450 4f917833 Sofia Papagiannaki
        return True
451 2715ade4 Sofia Papagiannaki
452 d1e7d2b4 Sofia Papagiannaki
    def node_accounts(self, accounts=()):
453 d1e7d2b4 Sofia Papagiannaki
        s = select([self.nodes.c.path, self.nodes.c.node])
454 d1e7d2b4 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.node != 0,
455 d1e7d2b4 Sofia Papagiannaki
                         self.nodes.c.parent == 0))
456 d1e7d2b4 Sofia Papagiannaki
        if accounts:
457 d1e7d2b4 Sofia Papagiannaki
            s = s.where(self.nodes.c.path.in_(accounts))
458 d1e7d2b4 Sofia Papagiannaki
        r = self.conn.execute(s)
459 d1e7d2b4 Sofia Papagiannaki
        rows = r.fetchall()
460 d1e7d2b4 Sofia Papagiannaki
        r.close()
461 d1e7d2b4 Sofia Papagiannaki
        return rows
462 d1e7d2b4 Sofia Papagiannaki
463 ae6199c5 Sofia Papagiannaki
    def node_account_quotas(self):
464 ae6199c5 Sofia Papagiannaki
        s = select([self.nodes.c.path, self.policy.c.value])
465 ae6199c5 Sofia Papagiannaki
        s = s.where(and_(self.nodes.c.node != 0,
466 ae6199c5 Sofia Papagiannaki
                         self.nodes.c.parent == 0))
467 ae6199c5 Sofia Papagiannaki
        s = s.where(self.nodes.c.node == self.policy.c.node)
468 ae6199c5 Sofia Papagiannaki
        s = s.where(self.policy.c.key == 'quota')
469 ae6199c5 Sofia Papagiannaki
        r = self.conn.execute(s)
470 ae6199c5 Sofia Papagiannaki
        rows = r.fetchall()
471 ae6199c5 Sofia Papagiannaki
        r.close()
472 ae6199c5 Sofia Papagiannaki
        return dict(rows)
473 ae6199c5 Sofia Papagiannaki
474 d1e7d2b4 Sofia Papagiannaki
    def node_account_usage(self, account_node, cluster):
475 d1e7d2b4 Sofia Papagiannaki
        select_children = select(
476 d1e7d2b4 Sofia Papagiannaki
            [self.nodes.c.node]).where(self.nodes.c.parent == account_node)
477 d1e7d2b4 Sofia Papagiannaki
        select_descendants = select([self.nodes.c.node]).where(
478 d1e7d2b4 Sofia Papagiannaki
            or_(self.nodes.c.parent.in_(select_children),
479 d1e7d2b4 Sofia Papagiannaki
                self.nodes.c.node.in_(select_children)))
480 d1e7d2b4 Sofia Papagiannaki
        s = select([func.sum(self.versions.c.size)])
481 d1e7d2b4 Sofia Papagiannaki
        s = s.group_by(self.versions.c.cluster)
482 d1e7d2b4 Sofia Papagiannaki
        s = s.where(self.nodes.c.node == self.versions.c.node)
483 d1e7d2b4 Sofia Papagiannaki
        s = s.where(self.nodes.c.node.in_(select_descendants))
484 d1e7d2b4 Sofia Papagiannaki
        s = s.where(self.versions.c.cluster == cluster)
485 d1e7d2b4 Sofia Papagiannaki
        r = self.conn.execute(s)
486 d1e7d2b4 Sofia Papagiannaki
        usage = r.fetchone()[0]
487 d1e7d2b4 Sofia Papagiannaki
        r.close()
488 d1e7d2b4 Sofia Papagiannaki
        return usage
489 94bff756 Sofia Papagiannaki
490 5e7485da Antony Chazapis
    def policy_get(self, node):
491 6a82f89f Sofia Papagiannaki
        s = select([self.policy.c.key, self.policy.c.value],
492 2715ade4 Sofia Papagiannaki
                   self.policy.c.node == node)
493 5e7485da Antony Chazapis
        r = self.conn.execute(s)
494 5e7485da Antony Chazapis
        d = dict(r.fetchall())
495 5e7485da Antony Chazapis
        r.close()
496 5e7485da Antony Chazapis
        return d
497 2715ade4 Sofia Papagiannaki
498 5e7485da Antony Chazapis
    def policy_set(self, node, policy):
499 5e7485da Antony Chazapis
        #insert or replace
500 5e7485da Antony Chazapis
        for k, v in policy.iteritems():
501 6a82f89f Sofia Papagiannaki
            s = self.policy.update().where(and_(self.policy.c.node == node,
502 2715ade4 Sofia Papagiannaki
                                                self.policy.c.key == k))
503 2715ade4 Sofia Papagiannaki
            s = s.values(value=v)
504 5e7485da Antony Chazapis
            rp = self.conn.execute(s)
505 5e7485da Antony Chazapis
            rp.close()
506 5e7485da Antony Chazapis
            if rp.rowcount == 0:
507 6a82f89f Sofia Papagiannaki
                s = self.policy.insert()
508 2715ade4 Sofia Papagiannaki
                values = {'node': node, 'key': k, 'value': v}
509 5e7485da Antony Chazapis
                r = self.conn.execute(s, values)
510 5e7485da Antony Chazapis
                r.close()
511 2715ade4 Sofia Papagiannaki
512 4f917833 Sofia Papagiannaki
    def statistics_get(self, node, cluster=0):
513 4f917833 Sofia Papagiannaki
        """Return population, total size and last mtime
514 4f917833 Sofia Papagiannaki
           for all versions under node that belong to the cluster.
515 4f917833 Sofia Papagiannaki
        """
516 2715ade4 Sofia Papagiannaki
517 b43d44ad Sofia Papagiannaki
        s = select([self.statistics.c.population,
518 b43d44ad Sofia Papagiannaki
                    self.statistics.c.size,
519 b43d44ad Sofia Papagiannaki
                    self.statistics.c.mtime])
520 b43d44ad Sofia Papagiannaki
        s = s.where(and_(self.statistics.c.node == node,
521 b43d44ad Sofia Papagiannaki
                         self.statistics.c.cluster == cluster))
522 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
523 b43d44ad Sofia Papagiannaki
        row = r.fetchone()
524 b43d44ad Sofia Papagiannaki
        r.close()
525 b43d44ad Sofia Papagiannaki
        return row
526 2715ade4 Sofia Papagiannaki
527 4f917833 Sofia Papagiannaki
    def statistics_update(self, node, population, size, mtime, cluster=0):
528 4f917833 Sofia Papagiannaki
        """Update the statistics of the given node.
529 4f917833 Sofia Papagiannaki
           Statistics keep track the population, total
530 4f917833 Sofia Papagiannaki
           size of objects and mtime in the node's namespace.
531 4f917833 Sofia Papagiannaki
           May be zero or positive or negative numbers.
532 4f917833 Sofia Papagiannaki
        """
533 18266c93 Sofia Papagiannaki
        s = select([self.statistics.c.population, self.statistics.c.size],
534 2715ade4 Sofia Papagiannaki
                   and_(self.statistics.c.node == node,
535 2715ade4 Sofia Papagiannaki
                        self.statistics.c.cluster == cluster))
536 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
537 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
538 b43d44ad Sofia Papagiannaki
        rp.close()
539 18266c93 Sofia Papagiannaki
        if not r:
540 4f917833 Sofia Papagiannaki
            prepopulation, presize = (0, 0)
541 4f917833 Sofia Papagiannaki
        else:
542 4f917833 Sofia Papagiannaki
            prepopulation, presize = r
543 4f917833 Sofia Papagiannaki
        population += prepopulation
544 096a7c3b Sofia Papagiannaki
        population = max(population, 0)
545 4f917833 Sofia Papagiannaki
        size += presize
546 2715ade4 Sofia Papagiannaki
547 d3be972a Sofia Papagiannaki
        #insert or replace
548 70516d86 Sofia Papagiannaki
        #TODO better upsert
549 2715ade4 Sofia Papagiannaki
        u = self.statistics.update().where(and_(self.statistics.c.node == node,
550 2715ade4 Sofia Papagiannaki
                                           self.statistics.c.cluster == cluster))
551 d3be972a Sofia Papagiannaki
        u = u.values(population=population, size=size, mtime=mtime)
552 d3be972a Sofia Papagiannaki
        rp = self.conn.execute(u)
553 d3be972a Sofia Papagiannaki
        rp.close()
554 d3be972a Sofia Papagiannaki
        if rp.rowcount == 0:
555 d3be972a Sofia Papagiannaki
            ins = self.statistics.insert()
556 d3be972a Sofia Papagiannaki
            ins = ins.values(node=node, population=population, size=size,
557 d3be972a Sofia Papagiannaki
                             mtime=mtime, cluster=cluster)
558 d3be972a Sofia Papagiannaki
            self.conn.execute(ins).close()
559 2715ade4 Sofia Papagiannaki
560 4f917833 Sofia Papagiannaki
    def statistics_update_ancestors(self, node, population, size, mtime, cluster=0):
561 4f917833 Sofia Papagiannaki
        """Update the statistics of the given node's parent.
562 4f917833 Sofia Papagiannaki
           Then recursively update all parents up to the root.
563 4f917833 Sofia Papagiannaki
           Population is not recursive.
564 4f917833 Sofia Papagiannaki
        """
565 2715ade4 Sofia Papagiannaki
566 4f917833 Sofia Papagiannaki
        while True:
567 18266c93 Sofia Papagiannaki
            if node == ROOTNODE:
568 4f917833 Sofia Papagiannaki
                break
569 4f917833 Sofia Papagiannaki
            props = self.node_get_properties(node)
570 4f917833 Sofia Papagiannaki
            if props is None:
571 4f917833 Sofia Papagiannaki
                break
572 4f917833 Sofia Papagiannaki
            parent, path = props
573 4f917833 Sofia Papagiannaki
            self.statistics_update(parent, population, size, mtime, cluster)
574 4f917833 Sofia Papagiannaki
            node = parent
575 2715ade4 Sofia Papagiannaki
            population = 0  # Population isn't recursive
576 2715ade4 Sofia Papagiannaki
577 4f917833 Sofia Papagiannaki
    def statistics_latest(self, node, before=inf, except_cluster=0):
578 4f917833 Sofia Papagiannaki
        """Return population, total size and last mtime
579 4f917833 Sofia Papagiannaki
           for all latest versions under node that
580 4f917833 Sofia Papagiannaki
           do not belong to the cluster.
581 4f917833 Sofia Papagiannaki
        """
582 2715ade4 Sofia Papagiannaki
583 4f917833 Sofia Papagiannaki
        # The node.
584 4f917833 Sofia Papagiannaki
        props = self.node_get_properties(node)
585 4f917833 Sofia Papagiannaki
        if props is None:
586 4f917833 Sofia Papagiannaki
            return None
587 4f917833 Sofia Papagiannaki
        parent, path = props
588 2715ade4 Sofia Papagiannaki
589 4f917833 Sofia Papagiannaki
        # The latest version.
590 b43d44ad Sofia Papagiannaki
        s = select([self.versions.c.serial,
591 b43d44ad Sofia Papagiannaki
                    self.versions.c.node,
592 1c2fc0ff Antony Chazapis
                    self.versions.c.hash,
593 b43d44ad Sofia Papagiannaki
                    self.versions.c.size,
594 cf341da4 Antony Chazapis
                    self.versions.c.type,
595 d0aacf54 Sofia Papagiannaki
                    self.versions.c.source,
596 b43d44ad Sofia Papagiannaki
                    self.versions.c.mtime,
597 b43d44ad Sofia Papagiannaki
                    self.versions.c.muser,
598 25ae8b75 Antony Chazapis
                    self.versions.c.uuid,
599 f7d5b0cf Antony Chazapis
                    self.versions.c.checksum,
600 b43d44ad Sofia Papagiannaki
                    self.versions.c.cluster])
601 62d938dc Sofia Papagiannaki
        if before != inf:
602 585b75e7 Sofia Papagiannaki
            filtered = select([func.max(self.versions.c.serial)],
603 2715ade4 Sofia Papagiannaki
                              self.versions.c.node == node)
604 62d938dc Sofia Papagiannaki
            filtered = filtered.where(self.versions.c.mtime < before)
605 585b75e7 Sofia Papagiannaki
        else:
606 585b75e7 Sofia Papagiannaki
            filtered = select([self.nodes.c.latest_version],
607 2715ade4 Sofia Papagiannaki
                              self.versions.c.node == node)
608 b43d44ad Sofia Papagiannaki
        s = s.where(and_(self.versions.c.cluster != except_cluster,
609 62d938dc Sofia Papagiannaki
                         self.versions.c.serial == filtered))
610 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
611 b43d44ad Sofia Papagiannaki
        props = r.fetchone()
612 b43d44ad Sofia Papagiannaki
        r.close()
613 b43d44ad Sofia Papagiannaki
        if not props:
614 4f917833 Sofia Papagiannaki
            return None
615 4f917833 Sofia Papagiannaki
        mtime = props[MTIME]
616 2715ade4 Sofia Papagiannaki
617 4f917833 Sofia Papagiannaki
        # First level, just under node (get population).
618 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
619 b43d44ad Sofia Papagiannaki
        s = select([func.count(v.c.serial),
620 b43d44ad Sofia Papagiannaki
                    func.sum(v.c.size),
621 b43d44ad Sofia Papagiannaki
                    func.max(v.c.mtime)])
622 62d938dc Sofia Papagiannaki
        if before != inf:
623 585b75e7 Sofia Papagiannaki
            c1 = select([func.max(self.versions.c.serial)])
624 62d938dc Sofia Papagiannaki
            c1 = c1.where(self.versions.c.mtime < before)
625 585b75e7 Sofia Papagiannaki
            c1.where(self.versions.c.node == v.c.node)
626 585b75e7 Sofia Papagiannaki
        else:
627 585b75e7 Sofia Papagiannaki
            c1 = select([self.nodes.c.latest_version])
628 585b75e7 Sofia Papagiannaki
            c1.where(self.nodes.c.node == v.c.node)
629 b43d44ad Sofia Papagiannaki
        c2 = select([self.nodes.c.node], self.nodes.c.parent == node)
630 585b75e7 Sofia Papagiannaki
        s = s.where(and_(v.c.serial == c1,
631 b43d44ad Sofia Papagiannaki
                         v.c.cluster != except_cluster,
632 b43d44ad Sofia Papagiannaki
                         v.c.node.in_(c2)))
633 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
634 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
635 b43d44ad Sofia Papagiannaki
        rp.close()
636 b43d44ad Sofia Papagiannaki
        if not r:
637 4f917833 Sofia Papagiannaki
            return None
638 4f917833 Sofia Papagiannaki
        count = r[0]
639 4f917833 Sofia Papagiannaki
        mtime = max(mtime, r[2])
640 4f917833 Sofia Papagiannaki
        if count == 0:
641 4f917833 Sofia Papagiannaki
            return (0, 0, mtime)
642 2715ade4 Sofia Papagiannaki
643 4f917833 Sofia Papagiannaki
        # All children (get size and mtime).
644 92da0e5a Antony Chazapis
        # This is why the full path is stored.
645 b43d44ad Sofia Papagiannaki
        s = select([func.count(v.c.serial),
646 b43d44ad Sofia Papagiannaki
                    func.sum(v.c.size),
647 b43d44ad Sofia Papagiannaki
                    func.max(v.c.mtime)])
648 62d938dc Sofia Papagiannaki
        if before != inf:
649 585b75e7 Sofia Papagiannaki
            c1 = select([func.max(self.versions.c.serial)],
650 2715ade4 Sofia Papagiannaki
                        self.versions.c.node == v.c.node)
651 62d938dc Sofia Papagiannaki
            c1 = c1.where(self.versions.c.mtime < before)
652 585b75e7 Sofia Papagiannaki
        else:
653 585b75e7 Sofia Papagiannaki
            c1 = select([self.nodes.c.serial],
654 2715ade4 Sofia Papagiannaki
                        self.nodes.c.node == v.c.node)
655 2715ade4 Sofia Papagiannaki
        c2 = select([self.nodes.c.node], self.nodes.c.path.like(
656 2715ade4 Sofia Papagiannaki
            self.escape_like(path) + '%', escape='\\'))
657 b43d44ad Sofia Papagiannaki
        s = s.where(and_(v.c.serial == c1,
658 b43d44ad Sofia Papagiannaki
                         v.c.cluster != except_cluster,
659 b43d44ad Sofia Papagiannaki
                         v.c.node.in_(c2)))
660 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
661 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
662 b43d44ad Sofia Papagiannaki
        rp.close()
663 b43d44ad Sofia Papagiannaki
        if not r:
664 4f917833 Sofia Papagiannaki
            return None
665 4f917833 Sofia Papagiannaki
        size = r[1] - props[SIZE]
666 4f917833 Sofia Papagiannaki
        mtime = max(mtime, r[2])
667 4f917833 Sofia Papagiannaki
        return (count, size, mtime)
668 2715ade4 Sofia Papagiannaki
669 585b75e7 Sofia Papagiannaki
    def nodes_set_latest_version(self, node, serial):
670 585b75e7 Sofia Papagiannaki
        s = self.nodes.update().where(self.nodes.c.node == node)
671 2715ade4 Sofia Papagiannaki
        s = s.values(latest_version=serial)
672 585b75e7 Sofia Papagiannaki
        self.conn.execute(s).close()
673 2715ade4 Sofia Papagiannaki
674 f7d5b0cf Antony Chazapis
    def version_create(self, node, hash, size, type, source, muser, uuid, checksum, cluster=0):
675 4f917833 Sofia Papagiannaki
        """Create a new version from the given properties.
676 4f917833 Sofia Papagiannaki
           Return the (serial, mtime) of the new version.
677 4f917833 Sofia Papagiannaki
        """
678 2715ade4 Sofia Papagiannaki
679 4f917833 Sofia Papagiannaki
        mtime = time()
680 2715ade4 Sofia Papagiannaki
        s = self.versions.insert(
681 2715ade4 Sofia Papagiannaki
        ).values(node=node, hash=hash, size=size, type=type, source=source,
682 2715ade4 Sofia Papagiannaki
                 mtime=mtime, muser=muser, uuid=uuid, checksum=checksum, cluster=cluster)
683 b43d44ad Sofia Papagiannaki
        serial = self.conn.execute(s).inserted_primary_key[0]
684 4f917833 Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
685 2715ade4 Sofia Papagiannaki
686 585b75e7 Sofia Papagiannaki
        self.nodes_set_latest_version(node, serial)
687 2715ade4 Sofia Papagiannaki
688 4f917833 Sofia Papagiannaki
        return serial, mtime
689 2715ade4 Sofia Papagiannaki
690 8221c89d Sofia Papagiannaki
    def version_lookup(self, node, before=inf, cluster=0, all_props=True):
691 4f917833 Sofia Papagiannaki
        """Lookup the current version of the given node.
692 4f917833 Sofia Papagiannaki
           Return a list with its properties:
693 2715ade4 Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime,
694 2715ade4 Sofia Papagiannaki
            muser, uuid, checksum, cluster)
695 4f917833 Sofia Papagiannaki
           or None if the current version is not found in the given cluster.
696 4f917833 Sofia Papagiannaki
        """
697 2715ade4 Sofia Papagiannaki
698 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
699 8221c89d Sofia Papagiannaki
        if not all_props:
700 8221c89d Sofia Papagiannaki
            s = select([v.c.serial])
701 8221c89d Sofia Papagiannaki
        else:
702 8221c89d Sofia Papagiannaki
            s = select([v.c.serial, v.c.node, v.c.hash,
703 8221c89d Sofia Papagiannaki
                        v.c.size, v.c.type, v.c.source,
704 8221c89d Sofia Papagiannaki
                        v.c.mtime, v.c.muser, v.c.uuid,
705 8221c89d Sofia Papagiannaki
                        v.c.checksum, v.c.cluster])
706 62d938dc Sofia Papagiannaki
        if before != inf:
707 585b75e7 Sofia Papagiannaki
            c = select([func.max(self.versions.c.serial)],
708 2715ade4 Sofia Papagiannaki
                       self.versions.c.node == node)
709 62d938dc Sofia Papagiannaki
            c = c.where(self.versions.c.mtime < before)
710 585b75e7 Sofia Papagiannaki
        else:
711 585b75e7 Sofia Papagiannaki
            c = select([self.nodes.c.latest_version],
712 2715ade4 Sofia Papagiannaki
                       self.nodes.c.node == node)
713 b43d44ad Sofia Papagiannaki
        s = s.where(and_(v.c.serial == c,
714 b43d44ad Sofia Papagiannaki
                         v.c.cluster == cluster))
715 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
716 b43d44ad Sofia Papagiannaki
        props = r.fetchone()
717 b43d44ad Sofia Papagiannaki
        r.close()
718 70516d86 Sofia Papagiannaki
        if props:
719 4f917833 Sofia Papagiannaki
            return props
720 4f917833 Sofia Papagiannaki
        return None
721 2715ade4 Sofia Papagiannaki
722 8221c89d Sofia Papagiannaki
    def version_lookup_bulk(self, nodes, before=inf, cluster=0, all_props=True):
723 8221c89d Sofia Papagiannaki
        """Lookup the current versions of the given nodes.
724 8221c89d Sofia Papagiannaki
           Return a list with their properties:
725 8221c89d Sofia Papagiannaki
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
726 8221c89d Sofia Papagiannaki
        """
727 585b75e7 Sofia Papagiannaki
        if not nodes:
728 585b75e7 Sofia Papagiannaki
            return ()
729 8221c89d Sofia Papagiannaki
        v = self.versions.alias('v')
730 8221c89d Sofia Papagiannaki
        if not all_props:
731 8221c89d Sofia Papagiannaki
            s = select([v.c.serial])
732 8221c89d Sofia Papagiannaki
        else:
733 8221c89d Sofia Papagiannaki
            s = select([v.c.serial, v.c.node, v.c.hash,
734 8221c89d Sofia Papagiannaki
                        v.c.size, v.c.type, v.c.source,
735 8221c89d Sofia Papagiannaki
                        v.c.mtime, v.c.muser, v.c.uuid,
736 8221c89d Sofia Papagiannaki
                        v.c.checksum, v.c.cluster])
737 8221c89d Sofia Papagiannaki
        if before != inf:
738 585b75e7 Sofia Papagiannaki
            c = select([func.max(self.versions.c.serial)],
739 2715ade4 Sofia Papagiannaki
                       self.versions.c.node.in_(nodes))
740 8221c89d Sofia Papagiannaki
            c = c.where(self.versions.c.mtime < before)
741 585b75e7 Sofia Papagiannaki
            c = c.group_by(self.versions.c.node)
742 585b75e7 Sofia Papagiannaki
        else:
743 585b75e7 Sofia Papagiannaki
            c = select([self.nodes.c.latest_version],
744 2715ade4 Sofia Papagiannaki
                       self.nodes.c.node.in_(nodes))
745 8221c89d Sofia Papagiannaki
        s = s.where(and_(v.c.serial.in_(c),
746 8221c89d Sofia Papagiannaki
                         v.c.cluster == cluster))
747 07867f70 Sofia Papagiannaki
        s = s.order_by(v.c.node)
748 8221c89d Sofia Papagiannaki
        r = self.conn.execute(s)
749 8221c89d Sofia Papagiannaki
        rproxy = r.fetchall()
750 8221c89d Sofia Papagiannaki
        r.close()
751 8221c89d Sofia Papagiannaki
        return (tuple(row.values()) for row in rproxy)
752 2715ade4 Sofia Papagiannaki
753 4f917833 Sofia Papagiannaki
    def version_get_properties(self, serial, keys=(), propnames=_propnames):
754 4f917833 Sofia Papagiannaki
        """Return a sequence of values for the properties of
755 4f917833 Sofia Papagiannaki
           the version specified by serial and the keys, in the order given.
756 4f917833 Sofia Papagiannaki
           If keys is empty, return all properties in the order
757 f7d5b0cf Antony Chazapis
           (serial, node, hash, size, type, source, mtime, muser, uuid, checksum, cluster).
758 4f917833 Sofia Papagiannaki
        """
759 2715ade4 Sofia Papagiannaki
760 b43d44ad Sofia Papagiannaki
        v = self.versions.alias()
761 37bee317 Antony Chazapis
        s = select([v.c.serial, v.c.node, v.c.hash,
762 cf341da4 Antony Chazapis
                    v.c.size, v.c.type, v.c.source,
763 cf341da4 Antony Chazapis
                    v.c.mtime, v.c.muser, v.c.uuid,
764 f7d5b0cf Antony Chazapis
                    v.c.checksum, v.c.cluster], v.c.serial == serial)
765 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
766 b43d44ad Sofia Papagiannaki
        r = rp.fetchone()
767 b43d44ad Sofia Papagiannaki
        rp.close()
768 4f917833 Sofia Papagiannaki
        if r is None:
769 4f917833 Sofia Papagiannaki
            return r
770 2715ade4 Sofia Papagiannaki
771 4f917833 Sofia Papagiannaki
        if not keys:
772 4f917833 Sofia Papagiannaki
            return r
773 4f917833 Sofia Papagiannaki
        return [r[propnames[k]] for k in keys if k in propnames]
774 2715ade4 Sofia Papagiannaki
775 f7d5b0cf Antony Chazapis
    def version_put_property(self, serial, key, value):
776 f7d5b0cf Antony Chazapis
        """Set value for the property of version specified by key."""
777 2715ade4 Sofia Papagiannaki
778 f7d5b0cf Antony Chazapis
        if key not in _propnames:
779 f7d5b0cf Antony Chazapis
            return
780 f7d5b0cf Antony Chazapis
        s = self.versions.update()
781 f7d5b0cf Antony Chazapis
        s = s.where(self.versions.c.serial == serial)
782 f7d5b0cf Antony Chazapis
        s = s.values(**{key: value})
783 f7d5b0cf Antony Chazapis
        self.conn.execute(s).close()
784 2715ade4 Sofia Papagiannaki
785 4f917833 Sofia Papagiannaki
    def version_recluster(self, serial, cluster):
786 4f917833 Sofia Papagiannaki
        """Move the version into another cluster."""
787 2715ade4 Sofia Papagiannaki
788 4f917833 Sofia Papagiannaki
        props = self.version_get_properties(serial)
789 4f917833 Sofia Papagiannaki
        if not props:
790 4f917833 Sofia Papagiannaki
            return
791 4f917833 Sofia Papagiannaki
        node = props[NODE]
792 4f917833 Sofia Papagiannaki
        size = props[SIZE]
793 4f917833 Sofia Papagiannaki
        oldcluster = props[CLUSTER]
794 4f917833 Sofia Papagiannaki
        if cluster == oldcluster:
795 4f917833 Sofia Papagiannaki
            return
796 2715ade4 Sofia Papagiannaki
797 4f917833 Sofia Papagiannaki
        mtime = time()
798 4f917833 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, oldcluster)
799 4f917833 Sofia Papagiannaki
        self.statistics_update_ancestors(node, 1, size, mtime, cluster)
800 2715ade4 Sofia Papagiannaki
801 b43d44ad Sofia Papagiannaki
        s = self.versions.update()
802 b43d44ad Sofia Papagiannaki
        s = s.where(self.versions.c.serial == serial)
803 2715ade4 Sofia Papagiannaki
        s = s.values(cluster=cluster)
804 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
805 2715ade4 Sofia Papagiannaki
806 4f917833 Sofia Papagiannaki
    def version_remove(self, serial):
807 4f917833 Sofia Papagiannaki
        """Remove the serial specified."""
808 2715ade4 Sofia Papagiannaki
809 5161c672 Antony Chazapis
        props = self.version_get_properties(serial)
810 4f917833 Sofia Papagiannaki
        if not props:
811 4f917833 Sofia Papagiannaki
            return
812 4f917833 Sofia Papagiannaki
        node = props[NODE]
813 5161c672 Antony Chazapis
        hash = props[HASH]
814 4f917833 Sofia Papagiannaki
        size = props[SIZE]
815 4f917833 Sofia Papagiannaki
        cluster = props[CLUSTER]
816 2715ade4 Sofia Papagiannaki
817 4f917833 Sofia Papagiannaki
        mtime = time()
818 4f917833 Sofia Papagiannaki
        self.statistics_update_ancestors(node, -1, -size, mtime, cluster)
819 2715ade4 Sofia Papagiannaki
820 b43d44ad Sofia Papagiannaki
        s = self.versions.delete().where(self.versions.c.serial == serial)
821 b43d44ad Sofia Papagiannaki
        self.conn.execute(s).close()
822 2715ade4 Sofia Papagiannaki
823 585b75e7 Sofia Papagiannaki
        props = self.version_lookup(node, cluster=cluster, all_props=False)
824 585b75e7 Sofia Papagiannaki
        if props:
825 eecad161 Sofia Papagiannaki
            self.nodes_set_latest_version(node, serial)
826 2715ade4 Sofia Papagiannaki
827 813e42e5 Antony Chazapis
        return hash, size
828 2715ade4 Sofia Papagiannaki
829 059857e2 Antony Chazapis
    def attribute_get(self, serial, domain, keys=()):
830 4f917833 Sofia Papagiannaki
        """Return a list of (key, value) pairs of the version specified by serial.
831 4f917833 Sofia Papagiannaki
           If keys is empty, return all attributes.
832 4f917833 Sofia Papagiannaki
           Othwerise, return only those specified.
833 4f917833 Sofia Papagiannaki
        """
834 2715ade4 Sofia Papagiannaki
835 4f917833 Sofia Papagiannaki
        if keys:
836 b43d44ad Sofia Papagiannaki
            attrs = self.attributes.alias()
837 b43d44ad Sofia Papagiannaki
            s = select([attrs.c.key, attrs.c.value])
838 b43d44ad Sofia Papagiannaki
            s = s.where(and_(attrs.c.key.in_(keys),
839 059857e2 Antony Chazapis
                             attrs.c.serial == serial,
840 059857e2 Antony Chazapis
                             attrs.c.domain == domain))
841 4f917833 Sofia Papagiannaki
        else:
842 b43d44ad Sofia Papagiannaki
            attrs = self.attributes.alias()
843 b43d44ad Sofia Papagiannaki
            s = select([attrs.c.key, attrs.c.value])
844 059857e2 Antony Chazapis
            s = s.where(and_(attrs.c.serial == serial,
845 059857e2 Antony Chazapis
                             attrs.c.domain == domain))
846 b43d44ad Sofia Papagiannaki
        r = self.conn.execute(s)
847 b43d44ad Sofia Papagiannaki
        l = r.fetchall()
848 b43d44ad Sofia Papagiannaki
        r.close()
849 b43d44ad Sofia Papagiannaki
        return l
850 2715ade4 Sofia Papagiannaki
851 059857e2 Antony Chazapis
    def attribute_set(self, serial, domain, items):
852 4f917833 Sofia Papagiannaki
        """Set the attributes of the version specified by serial.
853 4f917833 Sofia Papagiannaki
           Receive attributes as an iterable of (key, value) pairs.
854 4f917833 Sofia Papagiannaki
        """
855 70516d86 Sofia Papagiannaki
        #insert or replace
856 70516d86 Sofia Papagiannaki
        #TODO better upsert
857 70516d86 Sofia Papagiannaki
        for k, v in items:
858 70516d86 Sofia Papagiannaki
            s = self.attributes.update()
859 70516d86 Sofia Papagiannaki
            s = s.where(and_(self.attributes.c.serial == serial,
860 059857e2 Antony Chazapis
                             self.attributes.c.domain == domain,
861 70516d86 Sofia Papagiannaki
                             self.attributes.c.key == k))
862 2715ade4 Sofia Papagiannaki
            s = s.values(value=v)
863 70516d86 Sofia Papagiannaki
            rp = self.conn.execute(s)
864 70516d86 Sofia Papagiannaki
            rp.close()
865 70516d86 Sofia Papagiannaki
            if rp.rowcount == 0:
866 70516d86 Sofia Papagiannaki
                s = self.attributes.insert()
867 059857e2 Antony Chazapis
                s = s.values(serial=serial, domain=domain, key=k, value=v)
868 70516d86 Sofia Papagiannaki
                self.conn.execute(s).close()
869 2715ade4 Sofia Papagiannaki
870 059857e2 Antony Chazapis
    def attribute_del(self, serial, domain, keys=()):
871 4f917833 Sofia Papagiannaki
        """Delete attributes of the version specified by serial.
872 4f917833 Sofia Papagiannaki
           If keys is empty, delete all attributes.
873 4f917833 Sofia Papagiannaki
           Otherwise delete those specified.
874 4f917833 Sofia Papagiannaki
        """
875 2715ade4 Sofia Papagiannaki
876 4f917833 Sofia Papagiannaki
        if keys:
877 b43d44ad Sofia Papagiannaki
            #TODO more efficient way to do this?
878 b43d44ad Sofia Papagiannaki
            for key in keys:
879 b43d44ad Sofia Papagiannaki
                s = self.attributes.delete()
880 b43d44ad Sofia Papagiannaki
                s = s.where(and_(self.attributes.c.serial == serial,
881 059857e2 Antony Chazapis
                                 self.attributes.c.domain == domain,
882 b43d44ad Sofia Papagiannaki
                                 self.attributes.c.key == key))
883 b43d44ad Sofia Papagiannaki
                self.conn.execute(s).close()
884 4f917833 Sofia Papagiannaki
        else:
885 b43d44ad Sofia Papagiannaki
            s = self.attributes.delete()
886 059857e2 Antony Chazapis
            s = s.where(and_(self.attributes.c.serial == serial,
887 059857e2 Antony Chazapis
                             self.attributes.c.domain == domain))
888 b43d44ad Sofia Papagiannaki
            self.conn.execute(s).close()
889 2715ade4 Sofia Papagiannaki
890 4f917833 Sofia Papagiannaki
    def attribute_copy(self, source, dest):
891 2715ade4 Sofia Papagiannaki
        s = select(
892 2715ade4 Sofia Papagiannaki
            [dest, self.attributes.c.domain,
893 2715ade4 Sofia Papagiannaki
                self.attributes.c.key, self.attributes.c.value],
894 b43d44ad Sofia Papagiannaki
            self.attributes.c.serial == source)
895 1b61dbc0 Sofia Papagiannaki
        rp = self.conn.execute(s)
896 1b61dbc0 Sofia Papagiannaki
        attributes = rp.fetchall()
897 1b61dbc0 Sofia Papagiannaki
        rp.close()
898 059857e2 Antony Chazapis
        for dest, domain, k, v in attributes:
899 dc9e6086 Sofia Papagiannaki
            #insert or replace
900 1b61dbc0 Sofia Papagiannaki
            s = self.attributes.update().where(and_(
901 1b61dbc0 Sofia Papagiannaki
                self.attributes.c.serial == dest,
902 059857e2 Antony Chazapis
                self.attributes.c.domain == domain,
903 1b61dbc0 Sofia Papagiannaki
                self.attributes.c.key == k))
904 1b61dbc0 Sofia Papagiannaki
            rp = self.conn.execute(s, value=v)
905 1b61dbc0 Sofia Papagiannaki
            rp.close()
906 1b61dbc0 Sofia Papagiannaki
            if rp.rowcount == 0:
907 1b61dbc0 Sofia Papagiannaki
                s = self.attributes.insert()
908 2715ade4 Sofia Papagiannaki
                values = {'serial': dest, 'domain': domain,
909 2715ade4 Sofia Papagiannaki
                          'key': k, 'value': v}
910 1b61dbc0 Sofia Papagiannaki
                self.conn.execute(s, values).close()
911 2715ade4 Sofia Papagiannaki
912 78348987 Sofia Papagiannaki
    def latest_attribute_keys(self, parent, domain, before=inf, except_cluster=0, pathq=None):
913 4f917833 Sofia Papagiannaki
        """Return a list with all keys pairs defined
914 4f917833 Sofia Papagiannaki
           for all latest versions under parent that
915 4f917833 Sofia Papagiannaki
           do not belong to the cluster.
916 4f917833 Sofia Papagiannaki
        """
917 2715ade4 Sofia Papagiannaki
918 78348987 Sofia Papagiannaki
        pathq = pathq or []
919 78348987 Sofia Papagiannaki
920 4f917833 Sofia Papagiannaki
        # TODO: Use another table to store before=inf results.
921 b43d44ad Sofia Papagiannaki
        a = self.attributes.alias('a')
922 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
923 b43d44ad Sofia Papagiannaki
        n = self.nodes.alias('n')
924 b43d44ad Sofia Papagiannaki
        s = select([a.c.key]).distinct()
925 62d938dc Sofia Papagiannaki
        if before != inf:
926 585b75e7 Sofia Papagiannaki
            filtered = select([func.max(self.versions.c.serial)])
927 62d938dc Sofia Papagiannaki
            filtered = filtered.where(self.versions.c.mtime < before)
928 585b75e7 Sofia Papagiannaki
            filtered = filtered.where(self.versions.c.node == v.c.node)
929 585b75e7 Sofia Papagiannaki
        else:
930 585b75e7 Sofia Papagiannaki
            filtered = select([self.nodes.c.latest_version])
931 585b75e7 Sofia Papagiannaki
            filtered = filtered.where(self.nodes.c.node == v.c.node)
932 585b75e7 Sofia Papagiannaki
        s = s.where(v.c.serial == filtered)
933 b43d44ad Sofia Papagiannaki
        s = s.where(v.c.cluster != except_cluster)
934 b43d44ad Sofia Papagiannaki
        s = s.where(v.c.node.in_(select([self.nodes.c.node],
935 2715ade4 Sofia Papagiannaki
                                        self.nodes.c.parent == parent)))
936 b43d44ad Sofia Papagiannaki
        s = s.where(a.c.serial == v.c.serial)
937 059857e2 Antony Chazapis
        s = s.where(a.c.domain == domain)
938 b43d44ad Sofia Papagiannaki
        s = s.where(n.c.node == v.c.node)
939 b43d44ad Sofia Papagiannaki
        conj = []
940 cf341da4 Antony Chazapis
        for path, match in pathq:
941 cf341da4 Antony Chazapis
            if match == MATCH_PREFIX:
942 2715ade4 Sofia Papagiannaki
                conj.append(
943 2715ade4 Sofia Papagiannaki
                    n.c.path.like(self.escape_like(path) + '%', escape='\\'))
944 cf341da4 Antony Chazapis
            elif match == MATCH_EXACT:
945 cf341da4 Antony Chazapis
                conj.append(n.c.path == path)
946 b43d44ad Sofia Papagiannaki
        if conj:
947 b43d44ad Sofia Papagiannaki
            s = s.where(or_(*conj))
948 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s)
949 70516d86 Sofia Papagiannaki
        rows = rp.fetchall()
950 b43d44ad Sofia Papagiannaki
        rp.close()
951 70516d86 Sofia Papagiannaki
        return [r[0] for r in rows]
952 2715ade4 Sofia Papagiannaki
953 4f917833 Sofia Papagiannaki
    def latest_version_list(self, parent, prefix='', delimiter=None,
954 4f917833 Sofia Papagiannaki
                            start='', limit=10000, before=inf,
955 371d907a Antony Chazapis
                            except_cluster=0, pathq=[], domain=None,
956 371d907a Antony Chazapis
                            filterq=[], sizeq=None, all_props=False):
957 4f917833 Sofia Papagiannaki
        """Return a (list of (path, serial) tuples, list of common prefixes)
958 4f917833 Sofia Papagiannaki
           for the current versions of the paths with the given parent,
959 4f917833 Sofia Papagiannaki
           matching the following criteria.
960 2715ade4 Sofia Papagiannaki

961 4f917833 Sofia Papagiannaki
           The property tuple for a version is returned if all
962 4f917833 Sofia Papagiannaki
           of these conditions are true:
963 2715ade4 Sofia Papagiannaki

964 4f917833 Sofia Papagiannaki
                a. parent matches
965 2715ade4 Sofia Papagiannaki

966 4f917833 Sofia Papagiannaki
                b. path > start
967 2715ade4 Sofia Papagiannaki

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

970 4f917833 Sofia Papagiannaki
                d. version is the max up to before
971 2715ade4 Sofia Papagiannaki

972 4f917833 Sofia Papagiannaki
                e. version is not in cluster
973 2715ade4 Sofia Papagiannaki

974 4f917833 Sofia Papagiannaki
                f. the path does not have the delimiter occuring
975 4f917833 Sofia Papagiannaki
                   after the prefix, or ends with the delimiter
976 2715ade4 Sofia Papagiannaki

977 4f917833 Sofia Papagiannaki
                g. serial matches the attribute filter query.
978 2715ade4 Sofia Papagiannaki

979 4f917833 Sofia Papagiannaki
                   A filter query is a comma-separated list of
980 4f917833 Sofia Papagiannaki
                   terms in one of these three forms:
981 2715ade4 Sofia Papagiannaki

982 4f917833 Sofia Papagiannaki
                   key
983 4f917833 Sofia Papagiannaki
                       an attribute with this key must exist
984 2715ade4 Sofia Papagiannaki

985 4f917833 Sofia Papagiannaki
                   !key
986 4f917833 Sofia Papagiannaki
                       an attribute with this key must not exist
987 2715ade4 Sofia Papagiannaki

988 4f917833 Sofia Papagiannaki
                   key ?op value
989 4f917833 Sofia Papagiannaki
                       the attribute with this key satisfies the value
990 4f917833 Sofia Papagiannaki
                       where ?op is one of ==, != <=, >=, <, >.
991 2715ade4 Sofia Papagiannaki

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

994 4f917833 Sofia Papagiannaki
           The list of common prefixes includes the prefixes
995 4f917833 Sofia Papagiannaki
           matching up to the first delimiter after prefix,
996 4f917833 Sofia Papagiannaki
           and are reported only once, as "virtual directories".
997 4f917833 Sofia Papagiannaki
           The delimiter is included in the prefixes.
998 2715ade4 Sofia Papagiannaki

999 4f917833 Sofia Papagiannaki
           If arguments are None, then the corresponding matching rule
1000 4f917833 Sofia Papagiannaki
           will always match.
1001 2715ade4 Sofia Papagiannaki

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

1004 371d907a Antony Chazapis
           If all_props is True, return all properties after path, not just serial.
1005 4f917833 Sofia Papagiannaki
        """
1006 2715ade4 Sofia Papagiannaki
1007 4f917833 Sofia Papagiannaki
        if not start or start < prefix:
1008 4f917833 Sofia Papagiannaki
            start = strprevling(prefix)
1009 4f917833 Sofia Papagiannaki
        nextling = strnextling(prefix)
1010 2715ade4 Sofia Papagiannaki
1011 b43d44ad Sofia Papagiannaki
        v = self.versions.alias('v')
1012 b43d44ad Sofia Papagiannaki
        n = self.nodes.alias('n')
1013 371d907a Antony Chazapis
        if not all_props:
1014 371d907a Antony Chazapis
            s = select([n.c.path, v.c.serial]).distinct()
1015 371d907a Antony Chazapis
        else:
1016 371d907a Antony Chazapis
            s = select([n.c.path,
1017 371d907a Antony Chazapis
                        v.c.serial, v.c.node, v.c.hash,
1018 371d907a Antony Chazapis
                        v.c.size, v.c.type, v.c.source,
1019 371d907a Antony Chazapis
                        v.c.mtime, v.c.muser, v.c.uuid,
1020 371d907a Antony Chazapis
                        v.c.checksum, v.c.cluster]).distinct()
1021 62d938dc Sofia Papagiannaki
        if before != inf:
1022 585b75e7 Sofia Papagiannaki
            filtered = select([func.max(self.versions.c.serial)])
1023 62d938dc Sofia Papagiannaki
            filtered = filtered.where(self.versions.c.mtime < before)
1024 585b75e7 Sofia Papagiannaki
        else:
1025 585b75e7 Sofia Papagiannaki
            filtered = select([self.nodes.c.latest_version])
1026 2715ade4 Sofia Papagiannaki
        s = s.where(
1027 2715ade4 Sofia Papagiannaki
            v.c.serial == filtered.where(self.nodes.c.node == v.c.node))
1028 b43d44ad Sofia Papagiannaki
        s = s.where(v.c.cluster != except_cluster)
1029 b43d44ad Sofia Papagiannaki
        s = s.where(v.c.node.in_(select([self.nodes.c.node],
1030 2715ade4 Sofia Papagiannaki
                                        self.nodes.c.parent == parent)))
1031 2715ade4 Sofia Papagiannaki
1032 b43d44ad Sofia Papagiannaki
        s = s.where(n.c.node == v.c.node)
1033 b43d44ad Sofia Papagiannaki
        s = s.where(and_(n.c.path > bindparam('start'), n.c.path < nextling))
1034 b43d44ad Sofia Papagiannaki
        conj = []
1035 cf341da4 Antony Chazapis
        for path, match in pathq:
1036 cf341da4 Antony Chazapis
            if match == MATCH_PREFIX:
1037 2715ade4 Sofia Papagiannaki
                conj.append(
1038 2715ade4 Sofia Papagiannaki
                    n.c.path.like(self.escape_like(path) + '%', escape='\\'))
1039 cf341da4 Antony Chazapis
            elif match == MATCH_EXACT:
1040 cf341da4 Antony Chazapis
                conj.append(n.c.path == path)
1041 b43d44ad Sofia Papagiannaki
        if conj:
1042 b43d44ad Sofia Papagiannaki
            s = s.where(or_(*conj))
1043 2715ade4 Sofia Papagiannaki
1044 7ff57991 Antony Chazapis
        if sizeq and len(sizeq) == 2:
1045 7ff57991 Antony Chazapis
            if sizeq[0]:
1046 7ff57991 Antony Chazapis
                s = s.where(v.c.size >= sizeq[0])
1047 7ff57991 Antony Chazapis
            if sizeq[1]:
1048 7ff57991 Antony Chazapis
                s = s.where(v.c.size < sizeq[1])
1049 2715ade4 Sofia Papagiannaki
1050 9d41b050 chazapis
        if domain and filterq:
1051 a847494c chazapis
            a = self.attributes.alias('a')
1052 3d13f97a Sofia Papagiannaki
            included, excluded, opers = parse_filters(filterq)
1053 3d13f97a Sofia Papagiannaki
            if included:
1054 a847494c chazapis
                subs = select([1])
1055 8877529a chazapis
                subs = subs.where(a.c.serial == v.c.serial).correlate(v)
1056 a847494c chazapis
                subs = subs.where(a.c.domain == domain)
1057 a847494c chazapis
                subs = subs.where(or_(*[a.c.key.op('=')(x) for x in included]))
1058 a847494c chazapis
                s = s.where(exists(subs))
1059 3d13f97a Sofia Papagiannaki
            if excluded:
1060 a847494c chazapis
                subs = select([1])
1061 8877529a chazapis
                subs = subs.where(a.c.serial == v.c.serial).correlate(v)
1062 a847494c chazapis
                subs = subs.where(a.c.domain == domain)
1063 a847494c chazapis
                subs = subs.where(or_(*[a.c.key.op('=')(x) for x in excluded]))
1064 a847494c chazapis
                s = s.where(not_(exists(subs)))
1065 9d41b050 chazapis
            if opers:
1066 6b2f11b4 Antony Chazapis
                for k, o, val in opers:
1067 6b2f11b4 Antony Chazapis
                    subs = select([1])
1068 6b2f11b4 Antony Chazapis
                    subs = subs.where(a.c.serial == v.c.serial).correlate(v)
1069 6b2f11b4 Antony Chazapis
                    subs = subs.where(a.c.domain == domain)
1070 2715ade4 Sofia Papagiannaki
                    subs = subs.where(
1071 2715ade4 Sofia Papagiannaki
                        and_(a.c.key.op('=')(k), a.c.value.op(o)(val)))
1072 6b2f11b4 Antony Chazapis
                    s = s.where(exists(subs))
1073 2715ade4 Sofia Papagiannaki
1074 9d41b050 chazapis
        s = s.order_by(n.c.path)
1075 2715ade4 Sofia Papagiannaki
1076 4f917833 Sofia Papagiannaki
        if not delimiter:
1077 b43d44ad Sofia Papagiannaki
            s = s.limit(limit)
1078 b43d44ad Sofia Papagiannaki
            rp = self.conn.execute(s, start=start)
1079 9d41b050 chazapis
            r = rp.fetchall()
1080 b43d44ad Sofia Papagiannaki
            rp.close()
1081 b43d44ad Sofia Papagiannaki
            return r, ()
1082 2715ade4 Sofia Papagiannaki
1083 4f917833 Sofia Papagiannaki
        pfz = len(prefix)
1084 4f917833 Sofia Papagiannaki
        dz = len(delimiter)
1085 4f917833 Sofia Papagiannaki
        count = 0
1086 4f917833 Sofia Papagiannaki
        prefixes = []
1087 4f917833 Sofia Papagiannaki
        pappend = prefixes.append
1088 4f917833 Sofia Papagiannaki
        matches = []
1089 4f917833 Sofia Papagiannaki
        mappend = matches.append
1090 2715ade4 Sofia Papagiannaki
1091 b43d44ad Sofia Papagiannaki
        rp = self.conn.execute(s, start=start)
1092 4f917833 Sofia Papagiannaki
        while True:
1093 b43d44ad Sofia Papagiannaki
            props = rp.fetchone()
1094 4f917833 Sofia Papagiannaki
            if props is None:
1095 4f917833 Sofia Papagiannaki
                break
1096 371d907a Antony Chazapis
            path = props[0]
1097 371d907a Antony Chazapis
            serial = props[1]
1098 4f917833 Sofia Papagiannaki
            idx = path.find(delimiter, pfz)
1099 2715ade4 Sofia Papagiannaki
1100 4f917833 Sofia Papagiannaki
            if idx < 0:
1101 4f917833 Sofia Papagiannaki
                mappend(props)
1102 4f917833 Sofia Papagiannaki
                count += 1
1103 4f917833 Sofia Papagiannaki
                if count >= limit:
1104 4f917833 Sofia Papagiannaki
                    break
1105 4f917833 Sofia Papagiannaki
                continue
1106 2715ade4 Sofia Papagiannaki
1107 4f917833 Sofia Papagiannaki
            if idx + dz == len(path):
1108 4f917833 Sofia Papagiannaki
                mappend(props)
1109 4f917833 Sofia Papagiannaki
                count += 1
1110 2715ade4 Sofia Papagiannaki
                continue  # Get one more, in case there is a path.
1111 d3be972a Sofia Papagiannaki
            pf = path[:idx + dz]
1112 d3be972a Sofia Papagiannaki
            pappend(pf)
1113 2715ade4 Sofia Papagiannaki
            if count >= limit:
1114 4f917833 Sofia Papagiannaki
                break
1115 2715ade4 Sofia Papagiannaki
1116 2715ade4 Sofia Papagiannaki
            rp = self.conn.execute(s, start=strnextling(pf))  # New start.
1117 9eb713e1 Sofia Papagiannaki
        rp.close()
1118 2715ade4 Sofia Papagiannaki
1119 4f917833 Sofia Papagiannaki
        return matches, prefixes
1120 2715ade4 Sofia Papagiannaki
1121 2bbf1544 Georgios D. Tsoukalas
    def latest_uuid(self, uuid, cluster):
1122 2bbf1544 Georgios D. Tsoukalas
        """Return the latest version of the given uuid and cluster.
1123 2bbf1544 Georgios D. Tsoukalas

1124 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1125 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1126 2bbf1544 Georgios D. Tsoukalas

1127 2bbf1544 Georgios D. Tsoukalas
        """
1128 2715ade4 Sofia Papagiannaki
1129 37bee317 Antony Chazapis
        v = self.versions.alias('v')
1130 37bee317 Antony Chazapis
        n = self.nodes.alias('n')
1131 37bee317 Antony Chazapis
        s = select([n.c.path, v.c.serial])
1132 37bee317 Antony Chazapis
        filtered = select([func.max(self.versions.c.serial)])
1133 2bbf1544 Georgios D. Tsoukalas
        filtered = filtered.where(self.versions.c.uuid == uuid)
1134 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
1135 2bbf1544 Georgios D. Tsoukalas
            filtered = filtered.where(self.versions.c.cluster == cluster)
1136 2bbf1544 Georgios D. Tsoukalas
        s = s.where(v.c.serial == filtered)
1137 37bee317 Antony Chazapis
        s = s.where(n.c.node == v.c.node)
1138 2715ade4 Sofia Papagiannaki
1139 37bee317 Antony Chazapis
        r = self.conn.execute(s)
1140 37bee317 Antony Chazapis
        l = r.fetchone()
1141 37bee317 Antony Chazapis
        r.close()
1142 37bee317 Antony Chazapis
        return l
1143 5576e6dd Sofia Papagiannaki
1144 5576e6dd Sofia Papagiannaki
    def domain_object_list(self, domain, cluster=None):
1145 5576e6dd Sofia Papagiannaki
        """Return a list of (path, property list, attribute dictionary)
1146 5576e6dd Sofia Papagiannaki
           for the objects in the specific domain and cluster.
1147 5576e6dd Sofia Papagiannaki
        """
1148 5576e6dd Sofia Papagiannaki
1149 5576e6dd Sofia Papagiannaki
        v = self.versions.alias('v')
1150 5576e6dd Sofia Papagiannaki
        n = self.nodes.alias('n')
1151 5576e6dd Sofia Papagiannaki
        a = self.attributes.alias('a')
1152 5576e6dd Sofia Papagiannaki
1153 5576e6dd Sofia Papagiannaki
        s = select([n.c.path, v.c.serial, v.c.node, v.c.hash, v.c.size,
1154 5576e6dd Sofia Papagiannaki
                    v.c.type, v.c.source, v.c.mtime, v.c.muser, v.c.uuid,
1155 5576e6dd Sofia Papagiannaki
                    v.c.checksum, v.c.cluster, a.c.key, a.c.value])
1156 5576e6dd Sofia Papagiannaki
        s = s.where(n.c.node == v.c.node)
1157 5576e6dd Sofia Papagiannaki
        s = s.where(n.c.latest_version == v.c.serial)
1158 5576e6dd Sofia Papagiannaki
        if cluster:
1159 5576e6dd Sofia Papagiannaki
            s = s.where(v.c.cluster == cluster)
1160 5576e6dd Sofia Papagiannaki
        s = s.where(v.c.serial == a.c.serial)
1161 5576e6dd Sofia Papagiannaki
        s = s.where(a.c.domain == domain)
1162 5576e6dd Sofia Papagiannaki
1163 5576e6dd Sofia Papagiannaki
        r = self.conn.execute(s)
1164 5576e6dd Sofia Papagiannaki
        rows = r.fetchall()
1165 5576e6dd Sofia Papagiannaki
        r.close()
1166 5576e6dd Sofia Papagiannaki
1167 5576e6dd Sofia Papagiannaki
        group_by = itemgetter(slice(12))
1168 5576e6dd Sofia Papagiannaki
        rows.sort(key = group_by)
1169 5576e6dd Sofia Papagiannaki
        groups = groupby(rows, group_by)
1170 5576e6dd Sofia Papagiannaki
        return [(k[0], k[1:], dict([i[12:] for i in data])) \
1171 5576e6dd Sofia Papagiannaki
            for (k, data) in groups]