Merge branch 'next'
[pithos] / snf-pithos-backend / pithos / backends / lib / sqlite / xfeatures.py
1 # Copyright 2011-2012 GRNET S.A. All rights reserved.
2
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34 from collections import defaultdict
35
36 from dbworker import DBWorker
37
38
39 class XFeatures(DBWorker):
40     """XFeatures are path properties that allow non-nested
41        inheritance patterns. Currently used for storing permissions.
42     """
43     
44     def __init__(self, **params):
45         DBWorker.__init__(self, **params)
46         execute = self.execute
47         
48         execute(""" pragma foreign_keys = on """)
49         
50         execute(""" create table if not exists xfeatures
51                           ( feature_id integer primary key,
52                             path       text ) """)
53         execute(""" create unique index if not exists idx_features_path
54                     on xfeatures(path) """)
55
56         execute(""" create table if not exists xfeaturevals
57                           ( feature_id integer,
58                             key        integer,
59                             value      text,
60                             primary key (feature_id, key, value)
61                             foreign key (feature_id) references xfeatures(feature_id)
62                             on delete cascade ) """)
63     
64 #     def xfeature_inherit(self, path):
65 #         """Return the (path, feature) inherited by the path, or None."""
66 #         
67 #         q = ("select path, feature_id from xfeatures "
68 #              "where path <= ? "
69 #              "and ? like path || '%' " # XXX: Escape like...
70 #              "order by path desc")
71 #         self.execute(q, (path, path))
72 #         return self.fetchall()
73     
74     def xfeature_get(self, path):
75         """Return feature for path."""
76         
77         q = "select feature_id from xfeatures where path = ?"
78         self.execute(q, (path,))
79         r = self.fetchone()
80         if r is not None:
81             return r[0]
82         return None
83     
84     def xfeature_create(self, path):
85         """Create and return a feature for path.
86            If the path has a feature, return it.
87         """
88         
89         feature = self.xfeature_get(path)
90         if feature is not None:
91             return feature
92         q = "insert into xfeatures (path) values (?)"
93         id = self.execute(q, (path,)).lastrowid
94         return id
95     
96     def xfeature_destroy(self, path):
97         """Destroy a feature and all its key, value pairs."""
98         
99         q = "delete from xfeatures where path = ?"
100         self.execute(q, (path,))
101     
102     def xfeature_destroy_bulk(self, paths):
103         """Destroy features and all their key, value pairs."""
104         
105         placeholders = ','.join('?' for path in paths)
106         q = "delete from xfeatures where path in (%s)" % placeholders
107         self.execute(q, paths)
108     
109     def feature_dict(self, feature):
110         """Return a dict mapping keys to list of values for feature."""
111         
112         q = "select key, value from xfeaturevals where feature_id = ?"
113         self.execute(q, (feature,))
114         d = defaultdict(list)
115         for key, value in self.fetchall():
116             d[key].append(value)
117         return d
118     
119     def feature_set(self, feature, key, value):
120         """Associate a key, value pair with a feature."""
121         
122         q = "insert or ignore into xfeaturevals (feature_id, key, value) values (?, ?, ?)"
123         self.execute(q, (feature, key, value))
124     
125     def feature_setmany(self, feature, key, values):
126         """Associate the given key, and values with a feature."""
127         
128         q = "insert or ignore into xfeaturevals (feature_id, key, value) values (?, ?, ?)"
129         self.executemany(q, ((feature, key, v) for v in values))
130     
131     def feature_unset(self, feature, key, value):
132         """Disassociate a key, value pair from a feature."""
133         
134         q = ("delete from xfeaturevals where "
135              "feature_id = ? and key = ? and value = ?")
136         self.execute(q, (feature, key, value))
137     
138     def feature_unsetmany(self, feature, key, values):
139         """Disassociate the key for the values given, from a feature."""
140         
141         q = ("delete from xfeaturevals where "
142              "feature_id = ? and key = ? and value = ?")
143         self.executemany(q, ((feature, key, v) for v in values))
144     
145     def feature_get(self, feature, key):
146         """Return the list of values for a key of a feature."""
147         
148         q = "select value from xfeaturevals where feature_id = ? and key = ?"
149         self.execute(q, (feature, key))
150         return [r[0] for r in self.fetchall()]
151     
152     def feature_clear(self, feature, key):
153         """Delete all key, value pairs for a key of a feature."""
154         
155         q = "delete from xfeaturevals where feature_id = ? and key = ?"
156         self.execute(q, (feature, key))