blob: bef7018614def01ddc4d78a54cbe34741f0e0be4 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""BitBake Persistent Data Store
2
3Used to store data in a central location such that other threads/tasks can
4access them at some future date. Acts as a convenience wrapper around sqlite,
5currently, providing a key/value store accessed by 'domain'.
6"""
7
8# Copyright (C) 2007 Richard Purdie
9# Copyright (C) 2010 Chris Larson <chris_larson@mentor.com>
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License version 2 as
13# published by the Free Software Foundation.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License along
21# with this program; if not, write to the Free Software Foundation, Inc.,
22# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24import collections
25import logging
26import os.path
27import sys
28import warnings
29from bb.compat import total_ordering
30from collections import Mapping
Brad Bishop6e60e8b2018-02-01 10:27:11 -050031import sqlite3
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032
33sqlversion = sqlite3.sqlite_version_info
34if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
35 raise Exception("sqlite3 version 3.3.0 or later is required.")
36
37
38logger = logging.getLogger("BitBake.PersistData")
39if hasattr(sqlite3, 'enable_shared_cache'):
40 try:
41 sqlite3.enable_shared_cache(True)
42 except sqlite3.OperationalError:
43 pass
44
45
46@total_ordering
47class SQLTable(collections.MutableMapping):
48 """Object representing a table/domain in the database"""
49 def __init__(self, cachefile, table):
50 self.cachefile = cachefile
51 self.table = table
52 self.cursor = connect(self.cachefile)
53
54 self._execute("CREATE TABLE IF NOT EXISTS %s(key TEXT, value TEXT);"
55 % table)
56
57 def _execute(self, *query):
58 """Execute a query, waiting to acquire a lock if necessary"""
59 count = 0
60 while True:
61 try:
62 return self.cursor.execute(*query)
63 except sqlite3.OperationalError as exc:
64 if 'database is locked' in str(exc) and count < 500:
65 count = count + 1
66 self.cursor.close()
67 self.cursor = connect(self.cachefile)
68 continue
69 raise
70
71 def __enter__(self):
72 self.cursor.__enter__()
73 return self
74
75 def __exit__(self, *excinfo):
76 self.cursor.__exit__(*excinfo)
77
78 def __getitem__(self, key):
79 data = self._execute("SELECT * from %s where key=?;" %
80 self.table, [key])
81 for row in data:
82 return row[1]
83 raise KeyError(key)
84
85 def __delitem__(self, key):
86 if key not in self:
87 raise KeyError(key)
88 self._execute("DELETE from %s where key=?;" % self.table, [key])
89
90 def __setitem__(self, key, value):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060091 if not isinstance(key, str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050092 raise TypeError('Only string keys are supported')
Patrick Williamsc0f7c042017-02-23 20:41:17 -060093 elif not isinstance(value, str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094 raise TypeError('Only string values are supported')
95
96 data = self._execute("SELECT * from %s where key=?;" %
97 self.table, [key])
98 exists = len(list(data))
99 if exists:
100 self._execute("UPDATE %s SET value=? WHERE key=?;" % self.table,
101 [value, key])
102 else:
103 self._execute("INSERT into %s(key, value) values (?, ?);" %
104 self.table, [key, value])
105
106 def __contains__(self, key):
107 return key in set(self)
108
109 def __len__(self):
110 data = self._execute("SELECT COUNT(key) FROM %s;" % self.table)
111 for row in data:
112 return row[0]
113
114 def __iter__(self):
115 data = self._execute("SELECT key FROM %s;" % self.table)
116 return (row[0] for row in data)
117
118 def __lt__(self, other):
119 if not isinstance(other, Mapping):
120 raise NotImplemented
121
122 return len(self) < len(other)
123
124 def get_by_pattern(self, pattern):
125 data = self._execute("SELECT * FROM %s WHERE key LIKE ?;" %
126 self.table, [pattern])
127 return [row[1] for row in data]
128
129 def values(self):
130 return list(self.itervalues())
131
132 def itervalues(self):
133 data = self._execute("SELECT value FROM %s;" % self.table)
134 return (row[0] for row in data)
135
136 def items(self):
137 return list(self.iteritems())
138
139 def iteritems(self):
140 return self._execute("SELECT * FROM %s;" % self.table)
141
142 def clear(self):
143 self._execute("DELETE FROM %s;" % self.table)
144
145 def has_key(self, key):
146 return key in self
147
148
149class PersistData(object):
150 """Deprecated representation of the bitbake persistent data store"""
151 def __init__(self, d):
152 warnings.warn("Use of PersistData is deprecated. Please use "
153 "persist(domain, d) instead.",
154 category=DeprecationWarning,
155 stacklevel=2)
156
157 self.data = persist(d)
158 logger.debug(1, "Using '%s' as the persistent data cache",
159 self.data.filename)
160
161 def addDomain(self, domain):
162 """
163 Add a domain (pending deprecation)
164 """
165 return self.data[domain]
166
167 def delDomain(self, domain):
168 """
169 Removes a domain and all the data it contains
170 """
171 del self.data[domain]
172
173 def getKeyValues(self, domain):
174 """
175 Return a list of key + value pairs for a domain
176 """
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600177 return list(self.data[domain].items())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178
179 def getValue(self, domain, key):
180 """
181 Return the value of a key for a domain
182 """
183 return self.data[domain][key]
184
185 def setValue(self, domain, key, value):
186 """
187 Sets the value of a key for a domain
188 """
189 self.data[domain][key] = value
190
191 def delValue(self, domain, key):
192 """
193 Deletes a key/value pair
194 """
195 del self.data[domain][key]
196
197def connect(database):
198 connection = sqlite3.connect(database, timeout=5, isolation_level=None)
199 connection.execute("pragma synchronous = off;")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500200 connection.text_factory = str
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 return connection
202
203def persist(domain, d):
204 """Convenience factory for SQLTable objects based upon metadata"""
205 import bb.utils
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500206 cachedir = (d.getVar("PERSISTENT_DIR") or
207 d.getVar("CACHE"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500208 if not cachedir:
209 logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
210 sys.exit(1)
211
212 bb.utils.mkdirhier(cachedir)
213 cachefile = os.path.join(cachedir, "bb_persist_data.sqlite3")
214 return SQLTable(cachefile, domain)