]> git.ipfire.org Git - pakfire.git/blame - python/pakfire/repository/database.py
Add proper handling of configuration files.
[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
MT
21
22import logging
23import os
5cc35aa4 24import random
2568a6d1 25import shutil
47a4cb89 26import sqlite3
66af936c 27import time
47a4cb89 28
c605d735
MT
29import pakfire.packages as packages
30
a2d1644c 31from pakfire.constants import *
85a1120f 32from pakfire.i18n import _
66af936c 33
b6da0663
MT
34class 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 42class 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
98class 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()