]>
Commit | Line | Data |
---|---|---|
47a4cb89 | 1 | #!/usr/bin/python |
b792d887 MT |
2 | ############################################################################### |
3 | # # | |
4 | # Pakfire - The IPFire package management system # | |
5 | # Copyright (C) 2011 Pakfire development team # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
47a4cb89 MT |
21 | |
22 | import logging | |
23 | import os | |
5cc35aa4 | 24 | import random |
2568a6d1 | 25 | import shutil |
47a4cb89 | 26 | import sqlite3 |
66af936c | 27 | import time |
47a4cb89 | 28 | |
c605d735 MT |
29 | import pakfire.packages as packages |
30 | ||
a2d1644c | 31 | from pakfire.constants import * |
85a1120f | 32 | from pakfire.i18n import _ |
66af936c | 33 | |
b6da0663 MT |
34 | class Cursor(sqlite3.Cursor): |
35 | def execute(self, *args, **kwargs): | |
36 | # For debugging of SQL queries. | |
37 | #print args, kwargs | |
38 | ||
39 | return sqlite3.Cursor.execute(self, *args, **kwargs) | |
40 | ||
41 | ||
47a4cb89 | 42 | class Database(object): |
3723913b MT |
43 | def __init__(self, pakfire, filename): |
44 | self.pakfire = pakfire | |
5cc35aa4 MT |
45 | self.filename = filename |
46 | ||
c605d735 | 47 | self._db = None |
47a4cb89 MT |
48 | |
49 | def __del__(self): | |
50 | if self._db: | |
47a4cb89 | 51 | self._db.close() |
c605d735 | 52 | self._db = None |
47a4cb89 MT |
53 | |
54 | def create(self): | |
55 | pass | |
56 | ||
85a1120f MT |
57 | def migrate(self): |
58 | pass | |
59 | ||
47a4cb89 | 60 | def open(self): |
c605d735 | 61 | if self._db is None: |
47a4cb89 MT |
62 | logging.debug("Open database %s" % self.filename) |
63 | ||
5cc35aa4 MT |
64 | dirname = os.path.dirname(self.filename) |
65 | if not os.path.exists(dirname): | |
66 | os.makedirs(dirname) | |
47a4cb89 | 67 | |
5cc35aa4 | 68 | database_exists = os.path.exists(self.filename) |
47a4cb89 MT |
69 | |
70 | # Make a connection to the database. | |
71 | self._db = sqlite3.connect(self.filename) | |
72 | self._db.row_factory = sqlite3.Row | |
73 | ||
85a1120f MT |
74 | # In the case, the database was not existant, it is |
75 | # filled with content. In case it has been there | |
76 | # we call the migrate method to update it if neccessary. | |
77 | if database_exists: | |
78 | self.migrate() | |
79 | else: | |
47a4cb89 MT |
80 | self.create() |
81 | ||
82 | def close(self): | |
c605d735 | 83 | self.__del__() |
5cc35aa4 | 84 | |
47a4cb89 | 85 | def commit(self): |
c605d735 | 86 | self.open() |
47a4cb89 MT |
87 | self._db.commit() |
88 | ||
89 | def cursor(self): | |
c605d735 | 90 | self.open() |
b6da0663 | 91 | return self._db.cursor(Cursor) |
47a4cb89 | 92 | |
a2d1644c | 93 | def executescript(self, *args, **kwargs): |
c605d735 | 94 | self.open() |
a2d1644c MT |
95 | return self._db.executescript(*args, **kwargs) |
96 | ||
2568a6d1 | 97 | |
c605d735 MT |
98 | class DatabaseLocal(Database): |
99 | def __init__(self, pakfire, repo): | |
100 | self.repo = repo | |
2568a6d1 | 101 | |
c605d735 MT |
102 | # Generate filename for package database |
103 | filename = os.path.join(pakfire.path, PACKAGES_DB) | |
104 | ||
85a1120f MT |
105 | # Cache format number. |
106 | self.__format = None | |
107 | ||
c605d735 MT |
108 | Database.__init__(self, pakfire, filename) |
109 | ||
85a1120f MT |
110 | # Check if we actually can open the database. |
111 | if not self.format in DATABASE_FORMATS_SUPPORTED: | |
112 | raise DatabaseFormatError, _("The format of the database is not supported by this version of pakfire.") | |
113 | ||
c605d735 MT |
114 | def __len__(self): |
115 | count = 0 | |
47a4cb89 | 116 | |
47a4cb89 | 117 | c = self.cursor() |
c605d735 MT |
118 | c.execute("SELECT COUNT(*) AS count FROM packages") |
119 | for row in c: | |
120 | count = row["count"] | |
121 | c.close() | |
47a4cb89 | 122 | |
c605d735 MT |
123 | return count |
124 | ||
85a1120f MT |
125 | @property |
126 | def format(self): | |
127 | if self.__format is None: | |
128 | c = self.cursor() | |
129 | ||
130 | c.execute("SELECT val FROM settings WHERE key = 'version' LIMIT 1") | |
131 | for row in c: | |
132 | try: | |
133 | self.__format = int(row["val"]) | |
134 | break | |
135 | except ValueError: | |
136 | pass | |
137 | ||
138 | c.close() | |
139 | ||
140 | return self.__format | |
141 | ||
c605d735 MT |
142 | def create(self): |
143 | c = self.cursor() | |
47a4cb89 | 144 | c.executescript(""" |
c605d735 MT |
145 | CREATE TABLE settings( |
146 | key TEXT, | |
147 | val TEXT | |
148 | ); | |
85a1120f | 149 | INSERT INTO settings(key, val) VALUES('version', '%s'); |
c605d735 | 150 | |
47a4cb89 | 151 | CREATE TABLE files( |
c2808056 | 152 | id INTEGER PRIMARY KEY, |
47a4cb89 | 153 | name TEXT, |
c2808056 | 154 | pkg INTEGER, |
47a4cb89 MT |
155 | size INTEGER, |
156 | type INTEGER, | |
c2808056 MT |
157 | config INTEGER, |
158 | mode INTEGER, | |
159 | user TEXT, | |
160 | `group` TEXT, | |
161 | hash1 TEXT, | |
162 | mtime INTEGER | |
47a4cb89 MT |
163 | ); |
164 | ||
165 | CREATE TABLE packages( | |
c2808056 | 166 | id INTEGER PRIMARY KEY, |
47a4cb89 MT |
167 | name TEXT, |
168 | epoch INTEGER, | |
169 | version TEXT, | |
170 | release TEXT, | |
c560c27a | 171 | arch TEXT, |
8537c16d | 172 | groups TEXT, |
fa6d335b | 173 | filename TEXT, |
ba8c383d | 174 | size INTEGER, |
47a4cb89 MT |
175 | hash1 TEXT, |
176 | provides TEXT, | |
177 | requires TEXT, | |
178 | conflicts TEXT, | |
179 | obsoletes TEXT, | |
180 | license TEXT, | |
181 | summary TEXT, | |
182 | description TEXT, | |
1317485d | 183 | uuid TEXT, |
85a1120f | 184 | vendor TEXT, |
47a4cb89 MT |
185 | build_id TEXT, |
186 | build_host TEXT, | |
0c665250 | 187 | build_date TEXT, |
c605d735 MT |
188 | build_time INTEGER, |
189 | installed INT, | |
190 | reason TEXT, | |
c07a3ca7 MT |
191 | repository TEXT |
192 | ); | |
193 | ||
194 | CREATE TABLE scriptlets( | |
195 | id INTEGER PRIMARY KEY, | |
196 | pkg INTEGER, | |
197 | action TEXT, | |
198 | scriptlet TEXT | |
199 | ); | |
200 | ||
201 | CREATE TABLE triggers( | |
202 | id INTEGER PRIMARY KEY, | |
203 | pkg INTEGER, | |
204 | dependency TEXT, | |
205 | scriptlet TEXT | |
47a4cb89 | 206 | ); |
85a1120f | 207 | """ % DATABASE_FORMAT) |
47a4cb89 | 208 | # XXX add some indexes here |
47a4cb89 MT |
209 | self.commit() |
210 | c.close() | |
211 | ||
85a1120f MT |
212 | def migrate(self): |
213 | # If we have already the latest version, there is nothing to do. | |
214 | if self.format == DATABASE_FORMAT: | |
215 | return | |
216 | ||
217 | logging.info(_("Migrating database from format %s to %s.") % (self.format, DATABASE_FORMAT)) | |
218 | ||
219 | # Get a database cursor. | |
220 | c = self.cursor() | |
221 | ||
222 | # 1) The vendor column was added. | |
223 | if self.format < 1: | |
224 | c.execute("ALTER TABLE packages ADD COLUMN vendor TEXT AFTER uuid") | |
225 | ||
c2808056 MT |
226 | if self.format < 2: |
227 | c.execute("ALTER TABLE files ADD COLUMN `config` INTEGER") | |
228 | c.execute("ALTER TABLE files ADD COLUMN `mode` INTEGER") | |
229 | c.execute("ALTER TABLE files ADD COLUMN `user` TEXT") | |
230 | c.execute("ALTER TABLE files ADD COLUMN `group` TEXT") | |
231 | c.execute("ALTER TABLE files ADD COLUMN `mtime` INTEGER") | |
232 | ||
85a1120f MT |
233 | # In the end, we can easily update the version of the database. |
234 | c.execute("UPDATE settings SET val = ? WHERE key = 'version'", (DATABASE_FORMAT,)) | |
235 | self.__format = DATABASE_FORMAT | |
236 | ||
c2808056 | 237 | self.commit() |
85a1120f MT |
238 | c.close() |
239 | ||
66af936c | 240 | def add_package(self, pkg, reason=None): |
fa6d335b MT |
241 | logging.debug("Adding package to database: %s" % pkg.friendly_name) |
242 | ||
243 | c = self.cursor() | |
fa6d335b | 244 | |
c605d735 MT |
245 | try: |
246 | c.execute(""" | |
247 | INSERT INTO packages( | |
248 | name, | |
249 | epoch, | |
250 | version, | |
251 | release, | |
252 | arch, | |
253 | groups, | |
254 | filename, | |
255 | size, | |
256 | hash1, | |
257 | provides, | |
258 | requires, | |
259 | conflicts, | |
260 | obsoletes, | |
261 | license, | |
262 | summary, | |
263 | description, | |
264 | uuid, | |
85a1120f | 265 | vendor, |
c605d735 MT |
266 | build_id, |
267 | build_host, | |
268 | build_date, | |
269 | build_time, | |
270 | installed, | |
271 | repository, | |
c07a3ca7 | 272 | reason |
85a1120f | 273 | ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", |
c605d735 MT |
274 | ( |
275 | pkg.name, | |
276 | pkg.epoch, | |
277 | pkg.version, | |
278 | pkg.release, | |
279 | pkg.arch, | |
280 | " ".join(pkg.groups), | |
281 | pkg.filename, | |
282 | pkg.size, | |
283 | pkg.hash1, | |
284 | " ".join(pkg.provides), | |
285 | " ".join(pkg.requires), | |
286 | " ".join(pkg.conflicts), | |
287 | " ".join(pkg.obsoletes), | |
288 | pkg.license, | |
289 | pkg.summary, | |
290 | pkg.description, | |
291 | pkg.uuid, | |
85a1120f | 292 | pkg.vendor or "", |
c605d735 MT |
293 | pkg.build_id, |
294 | pkg.build_host, | |
295 | pkg.build_date, | |
296 | pkg.build_time, | |
297 | time.time(), | |
298 | pkg.repo.name, | |
299 | reason or "", | |
c605d735 MT |
300 | ) |
301 | ) | |
66af936c | 302 | |
c605d735 | 303 | pkg_id = c.lastrowid |
66af936c | 304 | |
c2808056 MT |
305 | c.executemany("INSERT INTO files(`name`, `pkg`, `size`, `config`, `type`, `hash1`, `mode`, `user`, `group`, `mtime`)" |
306 | " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", | |
307 | ((f.name, pkg_id, f.size, f.is_config(), f.type, f.hash1, f.mode, f.user, f.group, f.mtime) for f in pkg.filelist)) | |
66af936c | 308 | |
c605d735 MT |
309 | except: |
310 | raise | |
66af936c | 311 | |
c605d735 MT |
312 | else: |
313 | self.commit() | |
66af936c | 314 | |
66af936c | 315 | c.close() |
fa6d335b | 316 | |
e871a081 MT |
317 | def rem_package(self, pkg): |
318 | logging.debug("Removing package from database: %s" % pkg.friendly_name) | |
319 | ||
320 | assert pkg.uuid | |
321 | ||
322 | # Get the ID of the package in the database. | |
323 | c = self.cursor() | |
6ca2a97f MT |
324 | #c.execute("SELECT id FROM packages WHERE uuid = ? LIMIT 1", (pkg.uuid,)) |
325 | c.execute("SELECT id FROM packages WHERE name = ? AND epoch = ? AND version = ?" | |
326 | " AND release = ? LIMIT 1", (pkg.name, pkg.epoch, pkg.version, pkg.release,)) | |
e871a081 MT |
327 | |
328 | id = None | |
329 | for row in c: | |
330 | id = row["id"] | |
331 | break | |
332 | assert id | |
333 | ||
334 | # First, delete all files from the database and then delete the pkg itself. | |
335 | c.execute("DELETE FROM files WHERE pkg = ?", (id,)) | |
336 | c.execute("DELETE FROM packages WHERE id = ?", (id,)) | |
337 | ||
338 | c.close() | |
339 | self.commit() | |
340 | ||
862bea4d MT |
341 | def get_package_by_id(self, id): |
342 | c = self.cursor() | |
343 | c.execute("SELECT * FROM packages WHERE id = ?", (id,)) | |
344 | ||
345 | try: | |
346 | for row in c: | |
347 | return packages.DatabasePackage(self.pakfire, self.repo, self, row) | |
348 | ||
349 | finally: | |
350 | c.close() | |
351 | ||
c605d735 MT |
352 | @property |
353 | def packages(self): | |
47a4cb89 MT |
354 | c = self.cursor() |
355 | ||
c605d735 | 356 | c.execute("SELECT * FROM packages ORDER BY name") |
47a4cb89 | 357 | |
c605d735 MT |
358 | for row in c: |
359 | yield packages.DatabasePackage(self.pakfire, self.repo, self, row) | |
47a4cb89 | 360 | |
66af936c | 361 | c.close() |
6ee3d6b9 MT |
362 | |
363 | def get_package_from_solv(self, solv_pkg): | |
364 | c = self.cursor() | |
365 | c.execute("SELECT * FROM packages WHERE uuid = ? LIMIT 1", (solv_pkg.uuid,)) | |
366 | ||
367 | try: | |
368 | for row in c: | |
369 | return packages.DatabasePackage(self.pakfire, self.repo, self, row) | |
370 | ||
371 | finally: | |
372 | c.close() |