Statistics
| Branch: | Tag: | Revision:

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

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

929 4f917833 Sofia Papagiannaki
           The property tuple for a version is returned if all
930 4f917833 Sofia Papagiannaki
           of these conditions are true:
931 2715ade4 Sofia Papagiannaki

932 4f917833 Sofia Papagiannaki
                a. parent matches
933 2715ade4 Sofia Papagiannaki

934 4f917833 Sofia Papagiannaki
                b. path > start
935 2715ade4 Sofia Papagiannaki

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

938 4f917833 Sofia Papagiannaki
                d. version is the max up to before
939 2715ade4 Sofia Papagiannaki

940 4f917833 Sofia Papagiannaki
                e. version is not in cluster
941 2715ade4 Sofia Papagiannaki

942 4f917833 Sofia Papagiannaki
                f. the path does not have the delimiter occuring
943 4f917833 Sofia Papagiannaki
                   after the prefix, or ends with the delimiter
944 2715ade4 Sofia Papagiannaki

945 4f917833 Sofia Papagiannaki
                g. serial matches the attribute filter query.
946 2715ade4 Sofia Papagiannaki

947 4f917833 Sofia Papagiannaki
                   A filter query is a comma-separated list of
948 4f917833 Sofia Papagiannaki
                   terms in one of these three forms:
949 2715ade4 Sofia Papagiannaki

950 4f917833 Sofia Papagiannaki
                   key
951 4f917833 Sofia Papagiannaki
                       an attribute with this key must exist
952 2715ade4 Sofia Papagiannaki

953 4f917833 Sofia Papagiannaki
                   !key
954 4f917833 Sofia Papagiannaki
                       an attribute with this key must not exist
955 2715ade4 Sofia Papagiannaki

956 4f917833 Sofia Papagiannaki
                   key ?op value
957 4f917833 Sofia Papagiannaki
                       the attribute with this key satisfies the value
958 4f917833 Sofia Papagiannaki
                       where ?op is one of ==, != <=, >=, <, >.
959 2715ade4 Sofia Papagiannaki

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

962 4f917833 Sofia Papagiannaki
           The list of common prefixes includes the prefixes
963 4f917833 Sofia Papagiannaki
           matching up to the first delimiter after prefix,
964 4f917833 Sofia Papagiannaki
           and are reported only once, as "virtual directories".
965 4f917833 Sofia Papagiannaki
           The delimiter is included in the prefixes.
966 2715ade4 Sofia Papagiannaki

967 4f917833 Sofia Papagiannaki
           If arguments are None, then the corresponding matching rule
968 4f917833 Sofia Papagiannaki
           will always match.
969 2715ade4 Sofia Papagiannaki

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

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

1096 2bbf1544 Georgios D. Tsoukalas
        Return a (path, serial) tuple.
1097 2bbf1544 Georgios D. Tsoukalas
        If cluster is None, all clusters are considered.
1098 2bbf1544 Georgios D. Tsoukalas

1099 2bbf1544 Georgios D. Tsoukalas
        """
1100 2715ade4 Sofia Papagiannaki
1101 37bee317 Antony Chazapis
        v = self.versions.alias('v')
1102 37bee317 Antony Chazapis
        n = self.nodes.alias('n')
1103 37bee317 Antony Chazapis
        s = select([n.c.path, v.c.serial])
1104 37bee317 Antony Chazapis
        filtered = select([func.max(self.versions.c.serial)])
1105 2bbf1544 Georgios D. Tsoukalas
        filtered = filtered.where(self.versions.c.uuid == uuid)
1106 2bbf1544 Georgios D. Tsoukalas
        if cluster is not None:
1107 2bbf1544 Georgios D. Tsoukalas
            filtered = filtered.where(self.versions.c.cluster == cluster)
1108 2bbf1544 Georgios D. Tsoukalas
        s = s.where(v.c.serial == filtered)
1109 37bee317 Antony Chazapis
        s = s.where(n.c.node == v.c.node)
1110 2715ade4 Sofia Papagiannaki
1111 37bee317 Antony Chazapis
        r = self.conn.execute(s)
1112 37bee317 Antony Chazapis
        l = r.fetchone()
1113 37bee317 Antony Chazapis
        r.close()
1114 37bee317 Antony Chazapis
        return l