summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/persist_data.py
blob: 00f49299453b1d5d1d16efbe406897880af77e3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# BitBake Persistent Data Store
#
# Copyright (C) 2007        Richard Purdie
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import bb, os
import bb.utils

try:
    import sqlite3
except ImportError:
    try:
        from pysqlite2 import dbapi2 as sqlite3
    except ImportError:
        bb.msg.fatal(bb.msg.domain.PersistData, "Importing sqlite3 and pysqlite2 failed, please install one of them. Python 2.5 or a 'python-pysqlite2' like package is likely to be what you need.")

sqlversion = sqlite3.sqlite_version_info
if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
    bb.msg.fatal(bb.msg.domain.PersistData, "sqlite3 version 3.3.0 or later is required.")

class PersistData:
    """
    BitBake Persistent Data Store

    Used to store data in a central location such that other threads/tasks can
    access them at some future date.

    The "domain" is used as a key to isolate each data pool and in this
    implementation corresponds to an SQL table. The SQL table consists of a
    simple key and value pair.

    Why sqlite? It handles all the locking issues for us.
    """
    def __init__(self, d, persistent_database_connection):
        if "connection" in persistent_database_connection:
            self.cursor = persistent_database_connection["connection"].cursor()
            return
        self.cachedir = bb.data.getVar("PERSISTENT_DIR", d, True) or bb.data.getVar("CACHE", d, True)
        if self.cachedir in [None, '']:
            bb.msg.fatal(bb.msg.domain.PersistData, "Please set the 'PERSISTENT_DIR' or 'CACHE' variable.")
        try:
            os.stat(self.cachedir)
        except OSError:
            bb.utils.mkdirhier(self.cachedir)

        self.cachefile = os.path.join(self.cachedir, "bb_persist_data.sqlite3")
        bb.msg.debug(1, bb.msg.domain.PersistData, "Using '%s' as the persistent data cache" % self.cachefile)

        connection = sqlite3.connect(self.cachefile, timeout=5, isolation_level=None)
        persistent_database_connection["connection"] = connection
        self.cursor = persistent_database_connection["connection"].cursor()

    def addDomain(self, domain):
        """
        Should be called before any domain is used
        Creates it if it doesn't exist.
        """
        self._execute("CREATE TABLE IF NOT EXISTS %s(key TEXT, value TEXT);" % domain)

    def delDomain(self, domain):
        """
        Removes a domain and all the data it contains
        """
        self._execute("DROP TABLE IF EXISTS %s;" % domain)

    def getKeyValues(self, domain):
        """
        Return a list of key + value pairs for a domain
        """
        ret = {}
        data = self._execute("SELECT key, value from %s;" % domain)
        for row in data:
            ret[str(row[0])] = str(row[1])

        return ret

    def getValue(self, domain, key):
        """
        Return the value of a key for a domain
        """
        data = self._execute("SELECT * from %s where key=?;" % domain, [key])
        for row in data:
            return row[1]

    def setValue(self, domain, key, value):
        """
        Sets the value of a key for a domain
        """
        data = self._execute("SELECT * from %s where key=?;" % domain, [key])
        rows = 0
        for row in data:
            rows = rows + 1
        if rows:
            self._execute("UPDATE %s SET value=? WHERE key=?;" % domain, [value, key])
        else:
            self._execute("INSERT into %s(key, value) values (?, ?);" % domain, [key, value])

    def delValue(self, domain, key):
        """
        Deletes a key/value pair
        """
        self._execute("DELETE from %s where key=?;" % domain, [key])

    #
    # We wrap the sqlite execute calls as on contended machines or single threaded 
    # systems we can have multiple processes trying to access the DB at once and it seems
    # sqlite sometimes doesn't wait for the timeout. We therefore loop but put in an 
    # emergency brake too
    #
    def _execute(self, *query):
        count = 0
        while True:
            try:
                ret = self.cursor.execute(*query)
                #print "Had to retry %s times" % count
                return ret
            except sqlite3.OperationalError as e:
                if 'database is locked' in str(e) and count < 500:
                    count = count + 1
                    continue
                raise