2 ###############################################################################
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
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. #
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. #
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/>. #
20 ###############################################################################
29 log
= logging
.getLogger("pakfire")
31 import pakfire
.packages
as packages
33 from pakfire
.constants
import *
34 from pakfire
.errors
import *
35 from pakfire
.i18n
import _
37 class Database(object):
38 def __init__(self
, pakfire
, filename
):
39 self
.pakfire
= pakfire
40 self
.filename
= filename
64 log
.debug("Open database %s" % self
.filename
)
66 dirname
= os
.path
.dirname(self
.filename
)
67 if not os
.path
.exists(dirname
):
70 database_exists
= os
.path
.exists(self
.filename
)
72 # Make a connection to the database.
73 self
._db
= sqlite3
.connect(self
.filename
)
74 self
._db
.row_factory
= sqlite3
.Row
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.
95 return self
._db
.cursor()
97 def executescript(self
, *args
, **kwargs
):
99 return self
._db
.executescript(*args
, **kwargs
)
102 class DatabaseLocal(Database
):
103 def __init__(self
, pakfire
, repo
):
106 # Generate filename for package database
107 filename
= os
.path
.join(pakfire
.path
, PACKAGES_DB
)
109 # Cache format number.
112 Database
.__init
__(self
, pakfire
, filename
)
114 def initialize(self
):
118 # Check if we actually can open the database.
119 if not self
.format
in DATABASE_FORMATS_SUPPORTED
:
120 raise DatabaseFormatError
, _("The format of the database is not supported by this version of pakfire.")
126 c
.execute("SELECT COUNT(*) AS count FROM packages")
135 if self
.__format
is None:
138 c
.execute("SELECT val FROM settings WHERE key = 'version' LIMIT 1")
141 self
.__format
= int(row
["val"])
153 CREATE TABLE settings(
157 INSERT INTO settings(key, val) VALUES('version', '%s');
160 id INTEGER PRIMARY KEY,
174 CREATE INDEX files_pkg_index ON files(pkg);
176 CREATE TABLE packages(
177 id INTEGER PRIMARY KEY,
201 CREATE INDEX packages_name_index ON packages(name);
203 CREATE TABLE scriptlets(
204 id INTEGER PRIMARY KEY,
209 CREATE INDEX scriptlets_pkg_index ON scriptlets(pkg);
211 CREATE TABLE dependencies(
216 CREATE INDEX dependencies_pkg_index ON dependencies(pkg);
217 """ % DATABASE_FORMAT
)
218 # XXX add some indexes here
223 # If we have already the latest version, there is nothing to do.
224 if self
.format
== DATABASE_FORMAT
:
227 # Check if database version is supported.
228 if self
.format
> DATABASE_FORMAT
:
229 raise DatabaseError
, _("Cannot use database with version greater than %s.") % DATABASE_FORMAT
231 log
.info(_("Migrating database from format %(old)s to %(new)s.") % \
232 { "old" : self
.format
, "new" : DATABASE_FORMAT
})
234 # Get a database cursor.
237 # 1) The vendor column was added.
239 c
.execute("ALTER TABLE packages ADD COLUMN vendor TEXT AFTER uuid")
242 c
.execute("ALTER TABLE files ADD COLUMN `config` INTEGER")
243 c
.execute("ALTER TABLE files ADD COLUMN `mode` INTEGER")
244 c
.execute("ALTER TABLE files ADD COLUMN `user` TEXT")
245 c
.execute("ALTER TABLE files ADD COLUMN `group` TEXT")
246 c
.execute("ALTER TABLE files ADD COLUMN `mtime` INTEGER")
249 c
.execute("ALTER TABLE files ADD COLUMN `capabilities` TEXT")
252 c
.execute("ALTER TABLE packages ADD COLUMN recommends TEXT AFTER obsoletes")
253 c
.execute("ALTER TABLE packages ADD COLUMN suggests TEXT AFTER recommends")
256 c
.execute("ALTER TABLE files ADD COLUMN datafile INTEGER AFTER config")
259 c
.execute("ALTER TABLE packages ADD COLUMN inst_size INTEGER AFTER size")
263 CREATE TABLE dependencies(pkg INTEGER, type TEXT, dependency TEXT);
264 CREATE INDEX dependencies_pkg_index ON dependencies(pkg);
267 c
.execute("SELECT id, provides, requires, conflicts, obsoletes, recommends, suggests FROM packages")
271 (pkg_id
, provides
, requires
, conflicts
, obsoletes
, recommends
, suggests
) = pkg
274 ("provides", provides
),
275 ("requires", requires
),
276 ("conflicts", conflicts
),
277 ("obsoletes", obsoletes
),
278 ("recommends", recommends
),
279 ("suggests", suggests
),
282 for type, deps
in dependencies
:
283 c
.executemany("INSERT INTO dependencies(pkg, type, dependency) VALUES(?, ?, ?)",
284 ((pkg_id
, type, d
) for d
in deps
.splitlines()))
287 CREATE TABLE packages_(
288 id INTEGER PRIMARY KEY,
313 INSERT INTO packages_ SELECT id, name, epoch, version, release, arch, groups, filename,
314 size, inst_size, hash1, license, summary, description, uuid, vendor, build_id,
315 build_host, build_date, build_time, installed, reason, repository FROM packages;
318 ALTER TABLE packages_ RENAME TO packages;
322 CREATE INDEX files_pkg_index ON files(pkg);
323 CREATE INDEX scriptlets_pkg_index ON scriptlets(pkg);
324 CREATE INDEX packages_name_index ON packages(name);
327 # In the end, we can easily update the version of the database.
328 c
.execute("UPDATE settings SET val = ? WHERE key = 'version'", (DATABASE_FORMAT
,))
329 self
.__format
= DATABASE_FORMAT
334 def add_package(self
, pkg
, reason
=None):
335 log
.debug("Adding package to database: %s" % pkg
.friendly_name
)
341 INSERT INTO packages(
364 ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
371 " ".join(pkg
.groups
),
393 # Add all dependencies.
395 ("provides", pkg
.provides
),
396 ("requires", pkg
.requires
),
397 ("conflicts", pkg
.conflicts
),
398 ("obsoletes", pkg
.obsoletes
),
399 ("recommends", pkg
.recommends
),
400 ("suggests", pkg
.suggests
),
402 for type, deps
in dependencies
:
403 c
.executemany("INSERT INTO dependencies(pkg, type, dependency) VALUES(?, ?, ?)", ((pkg_id
, type, d
) for d
in deps
))
405 c
.executemany("INSERT INTO files(`name`, `pkg`, `size`, `config`, `datafile`, `type`, `hash1`, `mode`, `user`, `group`, `mtime`, `capabilities`)"
406 " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
407 ((f
.name
, pkg_id
, f
.size
, f
.is_config(), f
.is_datafile(), f
.type, f
.hash1
, f
.mode
, f
.user
, f
.group
, f
.mtime
, f
.capabilities
or "") for f
in pkg
.filelist
))
417 def rem_package(self
, pkg
):
418 assert isinstance(pkg
, packages
.DatabasePackage
), pkg
420 log
.debug("Removing package from database: %s" % pkg
.friendly_name
)
422 # First, delete all files from the database and then delete the pkg itself.
424 c
.execute("DELETE FROM files WHERE pkg = ?", (pkg
.id,))
425 c
.execute("DELETE FROM packages WHERE id = ?", (pkg
.id,))
430 def get_package_by_id(self
, id):
432 c
.execute("SELECT * FROM packages WHERE id = ?", (id,))
436 return packages
.DatabasePackage(self
.pakfire
, self
.repo
, self
, row
)
441 def get_package_by_uuid(self
, uuid
):
443 c
.execute("SELECT * FROM packages WHERE uuid = ?", (uuid
,))
447 return packages
.DatabasePackage(self
.pakfire
, self
.repo
, self
, row
)
454 c
= self
.db
.execute("SELECT * FROM packages ORDER BY name")
456 for row
in c
.fetchall():
457 yield packages
.DatabasePackage(self
.pakfire
, self
.repo
, self
, row
)
461 def get_filelist(self
):
462 c
= self
.db
.execute("SELECT name FROM files")
464 return [r
["name"] for r
in c
.fetchall()]
466 def get_package_from_solv(self
, solv_pkg
):
470 c
.execute("SELECT * FROM packages WHERE uuid = ? LIMIT 1", (solv_pkg
.uuid
,))
474 return packages
.DatabasePackage(self
.pakfire
, self
.repo
, self
, row
)