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