]> git.ipfire.org Git - people/ms/pakfire.git/blame - pakfire/repository/database.py
Enhance support for groups.
[people/ms/pakfire.git] / pakfire / repository / database.py
CommitLineData
47a4cb89
MT
1#!/usr/bin/python
2
3import logging
4import os
5cc35aa4 5import random
2568a6d1 6import shutil
47a4cb89 7import sqlite3
66af936c 8import time
47a4cb89 9
a2d1644c 10from pakfire.constants import *
66af936c 11
b6da0663
MT
12class Cursor(sqlite3.Cursor):
13 def execute(self, *args, **kwargs):
14 # For debugging of SQL queries.
15 #print args, kwargs
16
17 return sqlite3.Cursor.execute(self, *args, **kwargs)
18
19
47a4cb89 20class Database(object):
3723913b
MT
21 def __init__(self, pakfire, filename):
22 self.pakfire = pakfire
47a4cb89
MT
23 self._db = None
24
5cc35aa4
MT
25 self._tmp = False
26
27 if filename == ":memory:":
28 self._tmp = True
29
86973935 30 filename = "/tmp/.%s-db" % random.randint(0, 1024**2)
5cc35aa4
MT
31
32 self.filename = filename
33
47a4cb89
MT
34 self.open()
35
36 def __del__(self):
37 if self._db:
fa6d335b 38 #self._db.commit()
47a4cb89
MT
39 self._db.close()
40
41 def create(self):
42 pass
43
44 def open(self):
45 if not self._db:
46 logging.debug("Open database %s" % self.filename)
47
5cc35aa4
MT
48 dirname = os.path.dirname(self.filename)
49 if not os.path.exists(dirname):
50 os.makedirs(dirname)
47a4cb89 51
5cc35aa4 52 database_exists = os.path.exists(self.filename)
47a4cb89
MT
53
54 # Make a connection to the database.
55 self._db = sqlite3.connect(self.filename)
56 self._db.row_factory = sqlite3.Row
57
58 # Create the database if it was not there, yet.
59 if not database_exists:
60 self.create()
61
62 def close(self):
63 self._db.close()
2568a6d1 64 self._db = None
47a4cb89 65
5cc35aa4
MT
66 if self._tmp:
67 os.unlink(self.filename)
68
47a4cb89
MT
69 def commit(self):
70 self._db.commit()
71
72 def cursor(self):
b6da0663 73 return self._db.cursor(Cursor)
47a4cb89 74
a2d1644c
MT
75 def executescript(self, *args, **kwargs):
76 return self._db.executescript(*args, **kwargs)
77
2568a6d1
MT
78 def save(self, path):
79 """
a2d1644c 80 Save a copy of this database to a new one located at path.
2568a6d1 81 """
5cc35aa4 82 self.commit()
2568a6d1 83
5cc35aa4 84 shutil.copy2(self.filename, path)
2568a6d1 85
47a4cb89 86
fa6d335b 87class PackageDatabase(Database):
47a4cb89
MT
88 def create(self):
89 c = self.cursor()
90
91 c.executescript("""
92 CREATE TABLE files(
93 name TEXT,
94 pkg INTEGER,
95 size INTEGER,
96 type INTEGER,
66af936c 97 hash1 TEXT
47a4cb89
MT
98 );
99
100 CREATE TABLE packages(
101 id INTEGER PRIMARY KEY,
102 name TEXT,
103 epoch INTEGER,
104 version TEXT,
105 release TEXT,
c560c27a 106 arch TEXT,
8537c16d 107 groups TEXT,
fa6d335b 108 filename TEXT,
ba8c383d 109 size INTEGER,
47a4cb89
MT
110 hash1 TEXT,
111 provides TEXT,
112 requires TEXT,
113 conflicts TEXT,
114 obsoletes TEXT,
115 license TEXT,
116 summary TEXT,
117 description TEXT,
1317485d 118 uuid TEXT,
47a4cb89
MT
119 build_id TEXT,
120 build_host TEXT,
0c665250
MT
121 build_date TEXT,
122 build_time INTEGER
47a4cb89
MT
123 );
124 """)
125 # XXX add some indexes here
126
127 self.commit()
128 c.close()
129
130 def list_packages(self):
131 c = self.cursor()
132 c.execute("SELECT DISTINCT name FROM packages ORDER BY name")
133
134 for pkg in c:
135 yield pkg["name"]
136
137 c.close()
138
fa6d335b
MT
139 def package_exists(self, pkg):
140 return not self.get_id_by_pkg(pkg) is None
141
142 def get_id_by_pkg(self, pkg):
143 c = self.cursor()
144
145 c.execute("SELECT id FROM packages WHERE name = ? AND version = ? AND \
146 release = ? AND epoch = ? LIMIT 1", (pkg.name, pkg.version, pkg.release, pkg.epoch))
147
148 ret = None
149 for i in c:
150 ret = i["id"]
151 break
66af936c 152
fa6d335b
MT
153 c.close()
154
155 return ret
156
157 def add_package(self, pkg):
66af936c
MT
158 raise NotImplementedError
159
160
161class RemotePackageDatabase(PackageDatabase):
162 def add_package(self, pkg, reason=None):
fa6d335b
MT
163 if self.package_exists(pkg):
164 logging.debug("Skipping package which already exists in database: %s" % pkg.friendly_name)
165 return
166
167 logging.debug("Adding package to database: %s" % pkg.friendly_name)
168
66af936c
MT
169 filename = ""
170 if pkg.repo.local:
ab86bec4
MT
171 # Get the path relatively to the repository.
172 filename = pkg.filename[len(pkg.repo.path):]
173 # Strip leading / if any.
174 if filename.startswith("/"):
175 filename = filename[1:]
66af936c 176
fa6d335b
MT
177 c = self.cursor()
178 c.execute("""
179 INSERT INTO packages(
180 name,
181 epoch,
182 version,
183 release,
c560c27a 184 arch,
8537c16d 185 groups,
fa6d335b 186 filename,
e399ad3a 187 size,
66af936c 188 hash1,
fa6d335b 189 provides,
66af936c
MT
190 requires,
191 conflicts,
192 obsoletes,
193 license,
194 summary,
195 description,
1317485d 196 uuid,
66af936c
MT
197 build_id,
198 build_host,
0c665250
MT
199 build_date,
200 build_time
8537c16d 201 ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
fa6d335b
MT
202 (
203 pkg.name,
204 pkg.epoch,
205 pkg.version,
206 pkg.release,
c560c27a 207 pkg.arch,
8537c16d 208 " ".join(pkg.groups),
66af936c 209 filename,
e399ad3a 210 pkg.size,
66af936c 211 pkg.hash1,
fa6d335b
MT
212 " ".join(pkg.provides),
213 " ".join(pkg.requires),
66af936c
MT
214 " ".join(pkg.conflicts),
215 " ".join(pkg.obsoletes),
216 pkg.license,
217 pkg.summary,
218 pkg.description,
1317485d 219 pkg.uuid,
66af936c
MT
220 pkg.build_id,
221 pkg.build_host,
0c665250
MT
222 pkg.build_date,
223 pkg.build_time,
fa6d335b
MT
224 )
225 )
fa6d335b 226 self.commit()
66af936c 227 c.close()
fa6d335b
MT
228
229 pkg_id = self.get_id_by_pkg(pkg)
230
231 c = self.cursor()
232 for file in pkg.filelist:
233 c.execute("INSERT INTO files(name, pkg) VALUES(?, ?)", (file, pkg_id))
234
66af936c 235 self.commit()
fa6d335b 236 c.close()
66af936c
MT
237
238 return pkg_id
239
240
241class LocalPackageDatabase(RemotePackageDatabase):
242 def __init__(self, pakfire):
243 # Generate filename for package database
244 filename = os.path.join(pakfire.path, PACKAGES_DB)
245
246 RemotePackageDatabase.__init__(self, pakfire, filename)
247
248 def create(self):
249 RemotePackageDatabase.create(self)
250
251 # Alter the database layout to store additional local information.
252 logging.debug("Altering database table for local information.")
253 c = self.cursor()
254 c.executescript("""
255 ALTER TABLE packages ADD COLUMN installed INT;
256 ALTER TABLE packages ADD COLUMN reason TEXT;
257 ALTER TABLE packages ADD COLUMN repository TEXT;
a5f5fced 258 ALTER TABLE packages ADD COLUMN scriptlet TEXT;
9c75e8ed 259 ALTER TABLE packages ADD COLUMN triggers TEXT;
66af936c 260 """)
fa6d335b 261 self.commit()
66af936c 262 c.close()
fa6d335b 263
66af936c
MT
264 def add_package(self, pkg, reason=None):
265 # Insert all the information to the database we have in the remote database
266 pkg_id = RemotePackageDatabase.add_package(self, pkg)
fa6d335b 267
66af936c 268 # then: add some more information
47a4cb89
MT
269 c = self.cursor()
270
66af936c
MT
271 # Save timestamp when the package was installed.
272 c.execute("UPDATE packages SET installed = ? WHERE id = ?", (time.time(), pkg_id))
47a4cb89 273
66af936c
MT
274 # Add repository information.
275 c.execute("UPDATE packages SET repository = ? WHERE id = ?", (pkg.repo.name, pkg_id))
47a4cb89 276
66af936c
MT
277 # Save reason of installation (if any).
278 if reason:
279 c.execute("UPDATE packages SET reason = ? WHERE id = ?", (reason, pkg_id))
47a4cb89 280
66af936c
MT
281 # Update the filename information.
282 c.execute("UPDATE packages SET filename = ? WHERE id = ?", (pkg.filename, pkg_id))
47a4cb89 283
a5f5fced
MT
284 # Add the scriptlet to database (needed to update or uninstall packages).
285 c.execute("UPDATE packages SET scriptlet = ? WHERE id = ?", (pkg.scriptlet, pkg_id))
286
9c75e8ed
MT
287 # Add triggers to the database.
288 triggers = " ".join(pkg.triggers)
289 c.execute("UPDATE packages SET triggers = ? WHERE id = ?", (triggers, pkg_id))
290
66af936c
MT
291 self.commit()
292 c.close()