[entropy.db] remove reverse dependencies SQL, generate reverse dependencies metadata dynamically (also using on-disk cache)
This avoids repository database write access requirements and improves reverse dependencies metadata reliability.
This commit is contained in:
@@ -243,7 +243,6 @@ help_opts_extended = [
|
||||
(2, 'vacuum', 2, _('remove installed packages repository internal indexes to save disk space')),
|
||||
(2, 'generate', 1, _('generate installed packages database using Source Package Manager repositories')),
|
||||
(2, 'resurrect', 1, _('generate installed packages database using files on the system [last hope]')),
|
||||
(2, 'revdeps', 1, _('regenerate reverse dependencies metadata')),
|
||||
(2, 'spmuids', 1, _('regenerate SPM UIDs map (SPM <-> Entropy packages)')),
|
||||
(2, 'spmsync', 1, _('makes Entropy aware of your Source Package Manager updated packages')),
|
||||
(2, 'backup', 2, _('backup the current Entropy installed packages database')),
|
||||
@@ -361,7 +360,6 @@ help_opts_extended = [
|
||||
|
||||
(2, 'deptest', 2, _('look for unsatisfied dependencies across community repositories')),
|
||||
(2, 'pkgtest', 2, _('verify the integrity of local package files')),
|
||||
(2, 'revdeps', 2, _('regenerate the reverse dependencies metadata')),
|
||||
|
||||
None,
|
||||
(1, 'ugc', 2, _('handles User Generated Content features')),
|
||||
@@ -560,16 +558,6 @@ def _do_text_community(main_cmd, options):
|
||||
server_reagent.Entropy.close_repositories()
|
||||
rc = 0
|
||||
|
||||
elif sub_cmd == "revdeps":
|
||||
try:
|
||||
import server_reagent
|
||||
except ImportError:
|
||||
print_error(darkgreen(comm_err_msg))
|
||||
rc = 1
|
||||
else:
|
||||
rc = server_reagent.Entropy.generate_reverse_dependencies_metadata()
|
||||
server_reagent.Entropy.close_repositories()
|
||||
|
||||
return rc
|
||||
|
||||
def _do_text_cleanup(main_cmd, options):
|
||||
|
||||
@@ -88,9 +88,6 @@ def database(options):
|
||||
elif cmd == "resurrect":
|
||||
return _database_resurrect(etp_client)
|
||||
|
||||
elif cmd == "revdeps":
|
||||
return _database_revdeps(etp_client)
|
||||
|
||||
elif cmd in ("counters", "spmuids",):
|
||||
if cmd == "counters":
|
||||
print_warning("")
|
||||
@@ -205,18 +202,6 @@ def _database_counters(entropy_client):
|
||||
_("Counters table regenerated. Look above for errors."),) ))
|
||||
return 0
|
||||
|
||||
def _database_revdeps(entropy_client):
|
||||
rc = test_clientdb(entropy_client)
|
||||
if rc is not None:
|
||||
return rc
|
||||
|
||||
print_info(red(" %s..." % (
|
||||
_("Regenerating reverse dependencies metadata"),) ))
|
||||
entropy_client.installed_repository().generateReverseDependenciesMetadata()
|
||||
print_info(red(" %s." % (
|
||||
_("Reverse dependencies metadata regenerated successfully"),) ))
|
||||
return 0
|
||||
|
||||
def _database_resurrect(entropy_client):
|
||||
|
||||
mytxt = "####### %s: %s" % (
|
||||
@@ -372,8 +357,6 @@ def _database_resurrect(entropy_client):
|
||||
|
||||
print_info(red(" %s." % (_("Database resurrected successfully"),)))
|
||||
|
||||
print_info(red(" %s..." % (_("Now generating reverse dependencies metadata"),)))
|
||||
entropy_client.installed_repository().generateReverseDependenciesMetadata()
|
||||
print_info(red(" %s..." % (_("Now indexing tables"),)))
|
||||
entropy_client.installed_repository().indexing = True
|
||||
entropy_client.installed_repository().createAllIndexes()
|
||||
@@ -718,9 +701,6 @@ def _database_generate(entropy_client):
|
||||
|
||||
print_info(red(" %s." % (_("All the Source Package Manager packages have been injected into Entropy database"),) ))
|
||||
|
||||
print_info(red(" %s..." % (
|
||||
_("Now generating reverse dependencies metadata"),) ))
|
||||
entropy_client.installed_repository().generateReverseDependenciesMetadata()
|
||||
print_info(red(" %s...") % (_("Now indexing tables"),) )
|
||||
entropy_client.installed_repository().indexing = True
|
||||
entropy_client.installed_repository().createAllIndexes()
|
||||
|
||||
@@ -2012,7 +2012,6 @@ def remove_packages(entropy_client, packages = None, atomsdata = None,
|
||||
if rc != 0:
|
||||
# generate reverse dependencies metadata now that's done
|
||||
# so we have fresh meat when queried with user privs
|
||||
installed_repo.generateReverseDependenciesMetadata(verbose = False)
|
||||
return -1, rc
|
||||
|
||||
# update resume cache
|
||||
@@ -2027,10 +2026,6 @@ def remove_packages(entropy_client, packages = None, atomsdata = None,
|
||||
del metaopts
|
||||
del Package
|
||||
|
||||
# generate reverse dependencies metadata now that's done
|
||||
# so we have fresh meat when queried with user privs
|
||||
installed_repo.generateReverseDependenciesMetadata(verbose = False)
|
||||
|
||||
print_info(red(" @@ ")+blue("%s." % (_("All done"),) ))
|
||||
return 0, 0
|
||||
|
||||
|
||||
@@ -780,10 +780,6 @@ generate installed packages database using Source Package Manager repositories
|
||||
|
||||
generate installed packages database using files on the system [last hope]
|
||||
|
||||
=item B<revdeps>
|
||||
|
||||
regenerate reverse dependencies metadata
|
||||
|
||||
=item B<spmuids>
|
||||
|
||||
regenerate SPM UIDs map (SPM <-> Entropy packages)
|
||||
@@ -1254,10 +1250,6 @@ look for unsatisfied dependencies across community repositories
|
||||
|
||||
verify the integrity of local package files
|
||||
|
||||
=item B<revdeps>
|
||||
|
||||
regenerate reverse dependencies metadata
|
||||
|
||||
|
||||
=item B<ugc>
|
||||
|
||||
|
||||
@@ -1574,9 +1574,6 @@ class Package:
|
||||
if automerge_data:
|
||||
inst_repo.insertAutomergefiles(idpackage, automerge_data)
|
||||
|
||||
# clear depends table, this will make clientdb dependstable to be
|
||||
# regenerated during the next request (retrieveReverseDependencies)
|
||||
inst_repo.taintReverseDependenciesMetadata()
|
||||
return idpackage
|
||||
|
||||
def __filter_out_items_not_installed_from_content(self, pkg_data):
|
||||
|
||||
+112
-255
@@ -33,6 +33,7 @@ import threading
|
||||
import subprocess
|
||||
import warnings
|
||||
from sqlite3 import dbapi2
|
||||
from threading import RLock
|
||||
|
||||
from entropy.const import etpConst, const_setup_file, \
|
||||
const_isunicode, const_convert_to_unicode, const_get_buffer, \
|
||||
@@ -124,12 +125,6 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
dependency VARCHAR
|
||||
);
|
||||
|
||||
CREATE TABLE dependstable (
|
||||
iddependency INTEGER PRIMARY KEY,
|
||||
idpackage INTEGER,
|
||||
FOREIGN KEY(idpackage) REFERENCES baseinfo(idpackage) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE conflicts (
|
||||
idpackage INTEGER,
|
||||
conflict VARCHAR,
|
||||
@@ -415,6 +410,7 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
on closeDB()
|
||||
@type temporary: bool
|
||||
"""
|
||||
self.__live_cache_lock = RLock()
|
||||
self.__cursor_cache = {}
|
||||
self.__connection_cache = {}
|
||||
self._cleanup_stale_cur_conn_t = time.time()
|
||||
@@ -586,10 +582,11 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
self._cursor().execute('PRAGMA default_cache_size = %s' % (size,))
|
||||
|
||||
def __clearLiveCache(self, key):
|
||||
try:
|
||||
del self.__live_cache[key]
|
||||
except KeyError:
|
||||
pass
|
||||
with self.__live_cache_lock:
|
||||
try:
|
||||
del self.__live_cache[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def __del__(self):
|
||||
self.closeDB()
|
||||
@@ -2189,7 +2186,8 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
"""
|
||||
super(EntropyRepository, self).clearCache()
|
||||
|
||||
self.__live_cache.clear()
|
||||
with self.__live_cache_lock:
|
||||
self.__live_cache.clear()
|
||||
|
||||
def retrieveRepositoryUpdatesDigest(self, repository):
|
||||
"""
|
||||
@@ -3206,52 +3204,51 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
"""
|
||||
Reimplemented from EntropyRepositoryBase.
|
||||
"""
|
||||
# WARNING: never remove this, otherwise equo.db
|
||||
# (client database) dependstable will be always broken (trust me)
|
||||
# sanity check on the table
|
||||
c_tup = ("retrieveReverseDependencies_check",)
|
||||
if not self.__live_cache.get(c_tup):
|
||||
self.__live_cache[c_tup] = True
|
||||
if not self._isDependsTableSane(): # is empty, need generation
|
||||
self.generateReverseDependenciesMetadata(verbose = False)
|
||||
# always force commit, if possible, otherwise this, for
|
||||
# read-only repos will be called over and over.
|
||||
self.commitChanges(force = True)
|
||||
with self.__live_cache_lock:
|
||||
if "reverseDependenciesMetadata" not in self.__live_cache:
|
||||
self.__generateReverseDependenciesMetadata()
|
||||
|
||||
excluded_deptypes_query = ""
|
||||
if exclude_deptypes is not None:
|
||||
for dep_type in exclude_deptypes:
|
||||
excluded_deptypes_query += " AND dependencies.type != %d" % (
|
||||
dep_type,)
|
||||
excluded_deptypes_query = ""
|
||||
if exclude_deptypes is not None:
|
||||
for dep_type in exclude_deptypes:
|
||||
excluded_deptypes_query += " AND dependencies.type != %d" % (
|
||||
dep_type,)
|
||||
|
||||
rev_deps = self.__live_cache['reverseDependenciesMetadata']
|
||||
dep_ids = set((k for k, v in rev_deps.items() if package_id in v))
|
||||
if not dep_ids:
|
||||
if key_slot:
|
||||
return []
|
||||
return set()
|
||||
|
||||
dep_ids_str = ', '.join((str(x) for x in dep_ids))
|
||||
|
||||
table_name = self._getReverseDependenciesTable()
|
||||
if atoms:
|
||||
cur = self._cursor().execute("""
|
||||
SELECT baseinfo.atom FROM %s,dependencies,baseinfo
|
||||
WHERE %s.idpackage = (?) AND
|
||||
%s.iddependency = dependencies.iddependency AND
|
||||
baseinfo.idpackage = dependencies.idpackage %s""" % (
|
||||
table_name, table_name, table_name, excluded_deptypes_query,),
|
||||
(package_id,))
|
||||
SELECT baseinfo.atom FROM dependencies, baseinfo
|
||||
WHERE baseinfo.idpackage = dependencies.idpackage %s AND
|
||||
dependencies.iddependency IN ( %s )""" % (
|
||||
excluded_deptypes_query, dep_ids_str,))
|
||||
result = self._cur2set(cur)
|
||||
elif key_slot:
|
||||
cur = self._cursor().execute("""
|
||||
SELECT categories.category || "/" || baseinfo.name,baseinfo.slot
|
||||
FROM baseinfo,categories,%s,dependencies
|
||||
WHERE %s.idpackage = (?) AND
|
||||
%s.iddependency = dependencies.iddependency AND
|
||||
baseinfo.idpackage = dependencies.idpackage AND
|
||||
categories.idcategory = baseinfo.idcategory %s""" % (
|
||||
table_name, table_name, table_name, excluded_deptypes_query,),
|
||||
(package_id,))
|
||||
SELECT categories.category || "/" || baseinfo.name,baseinfo.slot
|
||||
FROM baseinfo, categories, dependencies
|
||||
WHERE baseinfo.idpackage = dependencies.idpackage AND
|
||||
categories.idcategory = baseinfo.idcategory %s AND
|
||||
dependencies.iddependency IN ( %s )""" % (
|
||||
excluded_deptypes_query, dep_ids_str,))
|
||||
result = cur.fetchall()
|
||||
elif excluded_deptypes_query:
|
||||
cur = self._cursor().execute("""
|
||||
SELECT dependencies.idpackage FROM dependencies
|
||||
WHERE %s AND dependencies.iddependency IN ( %s )""" % (
|
||||
excluded_deptypes_query.lstrip("AND "), dep_ids_str,))
|
||||
result = self._cur2set(cur)
|
||||
else:
|
||||
cur = self._cursor().execute("""
|
||||
SELECT dependencies.idpackage FROM %s,dependencies
|
||||
WHERE %s.idpackage = (?) AND
|
||||
%s.iddependency = dependencies.iddependency %s""" % (
|
||||
table_name, table_name, table_name, excluded_deptypes_query,),
|
||||
(package_id,))
|
||||
SELECT dependencies.idpackage FROM dependencies
|
||||
WHERE dependencies.iddependency IN ( %s )""" % (dep_ids_str,))
|
||||
result = self._cur2set(cur)
|
||||
|
||||
return result
|
||||
@@ -3265,24 +3262,23 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
"""
|
||||
Reimplemented from EntropyRepositoryBase.
|
||||
"""
|
||||
# WARNING: never remove this, otherwise equo.db (client database)
|
||||
# dependstable will be always broken (trust me)
|
||||
# sanity check on the table
|
||||
c_tup = ("retrieveUnusedPackageIds_check",)
|
||||
if not self.__live_cache.get(c_tup):
|
||||
self.__live_cache[c_tup] = True
|
||||
if not self._isDependsTableSane(): # is empty, need generation
|
||||
self.generateReverseDependenciesMetadata(verbose = False)
|
||||
# always force commit, if possible, otherwise this, for
|
||||
# read-only repos will be called over and over.
|
||||
self.commitChanges(force = True)
|
||||
with self.__live_cache_lock:
|
||||
if "reverseDependenciesMetadata" not in self.__live_cache:
|
||||
self.__generateReverseDependenciesMetadata()
|
||||
|
||||
rev_deps = self.__live_cache['reverseDependenciesMetadata']
|
||||
pkg_ids = set()
|
||||
for v in rev_deps.values():
|
||||
pkg_ids |= v
|
||||
if not pkg_ids:
|
||||
return []
|
||||
pkg_ids_str = ', '.join((str(x) for x in pkg_ids))
|
||||
|
||||
table_name = self._getReverseDependenciesTable()
|
||||
cur = self._cursor().execute("""
|
||||
SELECT idpackage FROM baseinfo
|
||||
WHERE idpackage NOT IN (SELECT idpackage FROM %s)
|
||||
SELECT idpackage FROM baseinfo
|
||||
WHERE idpackage NOT IN ( %s )
|
||||
ORDER BY atom
|
||||
""" % (table_name,))
|
||||
""" % (pkg_ids_str,))
|
||||
return self._cur2list(cur)
|
||||
|
||||
def _isAtomAvailable(self, atom):
|
||||
@@ -4312,9 +4308,6 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
if not self._doesTableExist("entropy_branch_migration"):
|
||||
self._createEntropyBranchMigrationTable()
|
||||
|
||||
if not self._doesTableExist("dependstable"):
|
||||
self._createDependsTable()
|
||||
|
||||
if not self._doesTableExist("settings"):
|
||||
self._createSettingsTable()
|
||||
|
||||
@@ -4327,11 +4320,12 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
"""
|
||||
Reimplemented from EntropyRepositoryBase.
|
||||
"""
|
||||
live_cache_id = ("validateDatabase",)
|
||||
cached = self.__live_cache.get(live_cache_id)
|
||||
if cached is not None:
|
||||
return
|
||||
self.__live_cache[live_cache_id] = True
|
||||
with self.__live_cache_lock:
|
||||
live_cache_id = ("validateDatabase",)
|
||||
cached = self.__live_cache.get(live_cache_id)
|
||||
if cached is not None:
|
||||
return
|
||||
self.__live_cache[live_cache_id] = True
|
||||
|
||||
# use sqlite3 pragma
|
||||
pingus = MtimePingus()
|
||||
@@ -4461,7 +4455,6 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
# clear caches
|
||||
self.clearCache()
|
||||
self.commitChanges()
|
||||
self.generateReverseDependenciesMetadata(verbose = False)
|
||||
dbconn.clearCache()
|
||||
|
||||
# verify both checksums, if they don't match, bomb out
|
||||
@@ -4594,37 +4587,41 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
return False
|
||||
return True
|
||||
|
||||
# speed up a bit if we already reported a table as existing
|
||||
c_tup = "_doesTableExist"
|
||||
cached = self.__live_cache.get(c_tup, {})
|
||||
if table in cached:
|
||||
return cached[table]
|
||||
with self.__live_cache_lock:
|
||||
# speed up a bit if we already reported a table as existing
|
||||
c_tup = "_doesTableExist"
|
||||
cached = self.__live_cache.get(c_tup, {})
|
||||
if table in cached:
|
||||
return cached[table]
|
||||
|
||||
cur = self._cursor().execute("""
|
||||
SELECT name FROM SQLITE_MASTER WHERE type = "table" AND name = (?)
|
||||
LIMIT 1
|
||||
""", (table,))
|
||||
rslt = cur.fetchone()
|
||||
exists = rslt is not None
|
||||
obj = self.__live_cache.setdefault(c_tup, {})
|
||||
obj[table] = exists
|
||||
|
||||
cur = self._cursor().execute("""
|
||||
SELECT name FROM SQLITE_MASTER WHERE type = "table" AND name = (?)
|
||||
LIMIT 1
|
||||
""", (table,))
|
||||
rslt = cur.fetchone()
|
||||
exists = rslt is not None
|
||||
obj = self.__live_cache.setdefault(c_tup, {})
|
||||
obj[table] = exists
|
||||
return exists
|
||||
|
||||
def _doesColumnInTableExist(self, table, column):
|
||||
|
||||
# speed up a bit if we already reported a column as existing
|
||||
c_tup = "_doesColumnInTableExist"
|
||||
d_tup = (table, column,)
|
||||
cached = self.__live_cache.get(c_tup, {})
|
||||
if d_tup in cached:
|
||||
return cached[d_tup]
|
||||
with self.__live_cache_lock:
|
||||
# speed up a bit if we already reported a column as existing
|
||||
c_tup = "_doesColumnInTableExist"
|
||||
d_tup = (table, column,)
|
||||
cached = self.__live_cache.get(c_tup, {})
|
||||
if d_tup in cached:
|
||||
return cached[d_tup]
|
||||
|
||||
cur = self._cursor().execute('PRAGMA table_info( %s )' % (table,))
|
||||
rslt = (x[1] for x in cur.fetchall())
|
||||
cur = self._cursor().execute('PRAGMA table_info( %s )' % (table,))
|
||||
rslt = (x[1] for x in cur.fetchall())
|
||||
|
||||
exists = column in rslt
|
||||
obj = self.__live_cache.setdefault(c_tup, {})
|
||||
obj[d_tup] = exists
|
||||
|
||||
exists = column in rslt
|
||||
obj = self.__live_cache.setdefault(c_tup, {})
|
||||
obj[d_tup] = exists
|
||||
return exists
|
||||
|
||||
def checksum(self, do_order = False, strict = True,
|
||||
@@ -4632,6 +4629,8 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
"""
|
||||
Reimplemented from EntropyRepositoryBase.
|
||||
"""
|
||||
# with self.__live_cache_lock:
|
||||
# Cache usage is thread safe here.
|
||||
c_tup = ("checksum", do_order, strict, strings, include_signatures,)
|
||||
cache = self.__live_cache.get(c_tup)
|
||||
if cache is not None:
|
||||
@@ -4759,61 +4758,6 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
DELETE FROM installedtable
|
||||
WHERE idpackage = (?)""", (package_id,))
|
||||
|
||||
def _createDependsTable(self, temporary = False):
|
||||
temp_txt = ""
|
||||
temp_txt_table = "dependstable"
|
||||
fk_data = """, FOREIGN KEY(idpackage) REFERENCES
|
||||
baseinfo(idpackage) ON DELETE CASCADE"""
|
||||
if temporary:
|
||||
temp_txt = "TEMPORARY"
|
||||
temp_txt_table = "dependstable_temp"
|
||||
fk_data = ""
|
||||
|
||||
self._cursor().executescript("""
|
||||
CREATE %s TABLE IF NOT EXISTS %s
|
||||
( iddependency INTEGER PRIMARY KEY, idpackage INTEGER%s );
|
||||
INSERT INTO %s VALUES (-1,NULL);
|
||||
""" % (temp_txt, temp_txt_table, fk_data, temp_txt_table,))
|
||||
if self.indexing:
|
||||
self._cursor().execute("""
|
||||
CREATE INDEX IF NOT EXISTS dependsindex%s_idpackage
|
||||
ON %s ( idpackage )
|
||||
""" % (temp_txt_table, temp_txt_table,))
|
||||
self.commitChanges()
|
||||
self.__clearLiveCache("_doesTableExist")
|
||||
|
||||
def _sanitizeDependsTable(self):
|
||||
table_name = self._getReverseDependenciesTable()
|
||||
self._cursor().execute("""
|
||||
DELETE FROM %s where iddependency = -1
|
||||
""" % (table_name,))
|
||||
self.__clearLiveCache("taintReverseDependenciesMetadata")
|
||||
self.commitChanges()
|
||||
|
||||
def _isDependsTableSane(self):
|
||||
|
||||
table_name = self._getReverseDependenciesTable()
|
||||
try:
|
||||
cur = self._cursor().execute("""
|
||||
SELECT iddependency FROM %s WHERE iddependency = -1
|
||||
""" % (table_name,))
|
||||
except (OperationalError,):
|
||||
return False # table does not exist, please regenerate and re-run
|
||||
|
||||
status = cur.fetchone()
|
||||
if status:
|
||||
return False
|
||||
|
||||
cur = self._cursor().execute("""
|
||||
SELECT count(iddependency) FROM %s
|
||||
""" % (table_name,))
|
||||
data = cur.fetchone()
|
||||
count = 0
|
||||
if data:
|
||||
count = data[0]
|
||||
|
||||
return count > 1
|
||||
|
||||
def storeXpakMetadata(self, *args, **kwargs):
|
||||
""" @deprecated """
|
||||
warnings.warn("deprecated call!")
|
||||
@@ -5254,7 +5198,7 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
"useflags", "keywords", "content", "counters", "sizes",
|
||||
"eclasses", "needed", "triggers", "systempackages", "injected",
|
||||
"installedtable", "automergefiles", "packagesignatures",
|
||||
"packagespmphases", "provided_libs", "dependstable"
|
||||
"packagespmphases", "provided_libs"
|
||||
)
|
||||
|
||||
done_something = False
|
||||
@@ -5578,119 +5522,32 @@ class EntropyRepository(EntropyRepositoryBase):
|
||||
""")
|
||||
self.__clearLiveCache("_doesTableExist")
|
||||
|
||||
def _addDependsRelationToDependsTable(self, iterable):
|
||||
# since this is not bulletproof (because user can mess with this
|
||||
# stuff via SPM), we need to IGNORE IntegrityError exceptions
|
||||
# caused by foreign constraint violation
|
||||
try:
|
||||
self._cursor().executemany("""
|
||||
INSERT or REPLACE into dependstable VALUES (?,?)""",
|
||||
iterable)
|
||||
except IntegrityError:
|
||||
# ouch, need to cope with that and execute each insert manually
|
||||
for iddep, package_id in iterable:
|
||||
try:
|
||||
self._cursor().execute("""
|
||||
INSERT or REPLACE into dependstable VALUES (?,?)""",
|
||||
(iddep, package_id,))
|
||||
except IntegrityError:
|
||||
continue
|
||||
except OperationalError:
|
||||
# ouch, we cannot write on db file, but still we need to store
|
||||
# the dep map and make unprivileged uids to get correct information
|
||||
# out of this, since it's stuff we can give away for free.
|
||||
# So, let's create a temp table, this will work.
|
||||
self._createDependsTable(temporary = True)
|
||||
self._cursor().executemany("""
|
||||
INSERT or REPLACE into dependstable_temp VALUES (?,?)""",
|
||||
iterable)
|
||||
# from now on, reverse dependencies should be considered
|
||||
# runtime generated only.
|
||||
self._temp_reverse_deps = True
|
||||
# no need to execute stuff below this
|
||||
self.__clearLiveCache("taintReverseDependenciesMetadata")
|
||||
return
|
||||
def __generateReverseDependenciesMetadata(self):
|
||||
"""
|
||||
Reverse dependencies dynamic metadata generation.
|
||||
"""
|
||||
checksum = self.checksum(strict = False)
|
||||
cache_key = "__generateReverseDependenciesMetadata_%s_%s" % (
|
||||
self.reponame, checksum,)
|
||||
rev_deps_data = self._cacher.pop(cache_key)
|
||||
if rev_deps_data is not None:
|
||||
self.__live_cache['reverseDependenciesMetadata'] = rev_deps_data
|
||||
return rev_deps_data
|
||||
|
||||
# prune old iddependencies
|
||||
# NOTE: maybe use ON DELETE CASCADE + foreign key reference?
|
||||
cur = self._cursor().execute("SELECT iddependency from dependstable")
|
||||
cur_iddeps = self._cur2set(cur)
|
||||
my_iddeps = set(x for x, y in iterable)
|
||||
to_be_pruned = cur_iddeps - my_iddeps
|
||||
if to_be_pruned:
|
||||
prune_list = [(x,) for x in to_be_pruned]
|
||||
self._cursor().executemany("""
|
||||
DELETE FROM dependstable WHERE iddependency = (?)
|
||||
""", prune_list)
|
||||
self.__clearLiveCache("taintReverseDependenciesMetadata")
|
||||
dep_data = {}
|
||||
for iddep, atom in self._listAllDependencies():
|
||||
|
||||
def _getReverseDependenciesTable(self):
|
||||
"""
|
||||
Internal method. When reverse dependencies table is not available
|
||||
and user has no privileges to make it automatically generated by
|
||||
the EntropyRepository logic, we need to fallback to a temporary table.
|
||||
This method just returns the available reverse dependency table that
|
||||
this instance should use. It does not check if user has write
|
||||
permissions but rather if the temporary table exists.
|
||||
"""
|
||||
if self._temp_reverse_deps:
|
||||
return "dependstable_temp"
|
||||
return "dependstable"
|
||||
|
||||
def taintReverseDependenciesMetadata(self):
|
||||
"""
|
||||
Reimplemented from EntropyRepositoryBase.
|
||||
"""
|
||||
cache_id = "taintReverseDependenciesMetadata"
|
||||
if cache_id in self.__live_cache:
|
||||
return
|
||||
table_name = self._getReverseDependenciesTable()
|
||||
try:
|
||||
self._cursor().executescript("""
|
||||
INSERT or IGNORE INTO %s VALUES (-1,NULL);
|
||||
""" % (table_name,))
|
||||
except (OperationalError,):
|
||||
# FIXME: backward compatibility
|
||||
pass
|
||||
self.__live_cache[cache_id] = True
|
||||
|
||||
def generateReverseDependenciesMetadata(self, verbose = True):
|
||||
"""
|
||||
Reimplemented from EntropyRepositoryBase.
|
||||
"""
|
||||
depends = self._listAllDependencies()
|
||||
count = 0
|
||||
total = len(depends)
|
||||
mydata = set()
|
||||
self.taintReverseDependenciesMetadata()
|
||||
self.commitChanges()
|
||||
for iddep, atom in depends:
|
||||
count += 1
|
||||
if iddep == -1:
|
||||
continue
|
||||
|
||||
if verbose and ((count == 0) or (count % 150 == 0) or \
|
||||
(count == total)):
|
||||
self.output( red("Resolving %s") % (atom,), importance = 0,
|
||||
level = "info", back = True, count = (count, total))
|
||||
|
||||
# not safe to use cache here, people messing with multiple
|
||||
# instances can make this crash
|
||||
package_id, rc = self.atomMatch(atom, useCache = False)
|
||||
if package_id != -1:
|
||||
mydata.add((iddep, package_id,))
|
||||
obj = dep_data.setdefault(iddep, set())
|
||||
obj.add(package_id)
|
||||
|
||||
# after this step, it'll be sane for sure
|
||||
if mydata:
|
||||
# NOTE: no need to call _sanitizeDependsTable()
|
||||
# _addDependsRelationToDependsTable() already removes
|
||||
# iddependency = -1
|
||||
self._addDependsRelationToDependsTable(mydata)
|
||||
else:
|
||||
self._sanitizeDependsTable()
|
||||
|
||||
super(EntropyRepository, self).generateReverseDependenciesMetadata(
|
||||
verbose = verbose)
|
||||
self.__live_cache['reverseDependenciesMetadata'] = dep_data
|
||||
self._cacher.push(cache_key, dep_data, async = False)
|
||||
|
||||
def moveSpmUidsToBranch(self, to_branch):
|
||||
"""
|
||||
|
||||
@@ -349,23 +349,6 @@ class EntropyRepositoryPlugin(object):
|
||||
"""
|
||||
return 0
|
||||
|
||||
def reverse_dependencies_tree_generation_hook(self,
|
||||
entropy_repository_instance):
|
||||
"""
|
||||
This hook is called inside
|
||||
EntropyRepository.generateReverseDependenciesMetadata() method at
|
||||
the very end of the function code.
|
||||
Every time that repository is "tainted" with new packages, sooner or
|
||||
later that function is called.
|
||||
|
||||
@param entropy_repository_instance: EntropyRepository instance
|
||||
@type entropy_repository_instance: EntropyRepository
|
||||
@return: execution status code, return nonzero for errors, this will
|
||||
raise a RepositoryPluginError exception.
|
||||
@rtype: int
|
||||
"""
|
||||
return 0
|
||||
|
||||
|
||||
class EntropyRepositoryBase(TextInterface, EntropyRepositoryPluginStore, object):
|
||||
"""
|
||||
@@ -3482,31 +3465,6 @@ class EntropyRepositoryBase(TextInterface, EntropyRepositoryPluginStore, object)
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def taintReverseDependenciesMetadata(self):
|
||||
"""
|
||||
Taint reverse (or inverse) dependencies metadata so that will be
|
||||
generated during the next request.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def generateReverseDependenciesMetadata(self, verbose = True):
|
||||
"""
|
||||
Regenerate reverse (or inverse) dependencies metadata.
|
||||
Attention: call this method from your subclass (AT THE END), otherwise
|
||||
EntropyRepositoryPlugins won't be notified.
|
||||
|
||||
@keyword verbose: enable verbosity
|
||||
@type verbose: bool
|
||||
"""
|
||||
plugins = self.get_plugins()
|
||||
for plugin_id in sorted(plugins):
|
||||
plug_inst = plugins[plugin_id]
|
||||
exec_rc = plug_inst.reverse_dependencies_tree_generation_hook(self)
|
||||
if exec_rc:
|
||||
raise RepositoryPluginError(
|
||||
"[reverse_dependencies_tree_generation_hook] %s: status: %s" % (
|
||||
plug_inst.get_id(), exec_rc,))
|
||||
|
||||
def moveSpmUidsToBranch(self, to_branch):
|
||||
"""
|
||||
Note: this is not intended for general audience.
|
||||
|
||||
@@ -409,17 +409,6 @@ class ServerEntropyRepositoryPlugin(EntropyRepositoryPlugin):
|
||||
return self.treeupdates_move_action_hook(entropy_repository_instance,
|
||||
package_id)
|
||||
|
||||
def reverse_dependencies_tree_generation_hook(self,
|
||||
entropy_repository_instance):
|
||||
# force commit even if readonly, this will allow
|
||||
# to automagically fix dependstable server side
|
||||
# we don't care much about syncing the
|
||||
# database since it's a quite trivial change
|
||||
entropy_repository_instance.commitChanges(force = True,
|
||||
no_plugins = True)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class ServerSystemSettingsPlugin(SystemSettingsPlugin):
|
||||
|
||||
@@ -4270,7 +4259,6 @@ class ServerRepositoryMixin:
|
||||
idpackages_added = set()
|
||||
to_be_injected = set()
|
||||
my_qa = self.QA()
|
||||
missing_deps_taint = False
|
||||
for package_filepath, inject in packages_data:
|
||||
|
||||
mycount += 1
|
||||
@@ -4308,13 +4296,11 @@ class ServerRepositoryMixin:
|
||||
header = bold(" !!! "),
|
||||
count = (mycount, maxcount,)
|
||||
)
|
||||
# reinit depends table
|
||||
self.generate_reverse_dependencies_metadata(repo)
|
||||
# reinit librarypathsidpackage table
|
||||
if idpackages_added:
|
||||
dbconn = self.open_server_repository(read_only = False,
|
||||
no_upload = True, repo = repo)
|
||||
missing_deps_taint = my_qa.test_missing_dependencies(
|
||||
my_qa.test_missing_dependencies(
|
||||
idpackages_added,
|
||||
dbconn,
|
||||
ask = ask,
|
||||
@@ -4331,15 +4317,9 @@ class ServerRepositoryMixin:
|
||||
if to_be_injected:
|
||||
self._inject_database_into_packages(to_be_injected,
|
||||
repo = repo)
|
||||
# reinit depends table
|
||||
if missing_deps_taint:
|
||||
self.generate_reverse_dependencies_metadata(repo)
|
||||
self.close_repositories()
|
||||
raise
|
||||
|
||||
# reinit depends table
|
||||
self.generate_reverse_dependencies_metadata(repo)
|
||||
|
||||
# make sure packages are really available, it can happen
|
||||
# after a previous failure to have garbage here
|
||||
dbconn = self.open_server_repository(just_reading = True, repo = repo)
|
||||
@@ -4349,7 +4329,7 @@ class ServerRepositoryMixin:
|
||||
if idpackages_added:
|
||||
dbconn = self.open_server_repository(read_only = False,
|
||||
no_upload = True, repo = repo)
|
||||
missing_deps_taint = my_qa.test_missing_dependencies(
|
||||
my_qa.test_missing_dependencies(
|
||||
idpackages_added,
|
||||
dbconn,
|
||||
ask = ask,
|
||||
@@ -4363,10 +4343,6 @@ class ServerRepositoryMixin:
|
||||
my_qa.test_reverse_dependencies_linking(idpackages_added, dbconn,
|
||||
repo = repo)
|
||||
|
||||
# reinit depends table
|
||||
if missing_deps_taint:
|
||||
self.generate_reverse_dependencies_metadata(repo)
|
||||
|
||||
# inject database into packages
|
||||
self._inject_database_into_packages(to_be_injected, repo = repo)
|
||||
|
||||
@@ -4475,13 +4451,6 @@ class ServerMiscMixin:
|
||||
elif isinstance(setting, dict):
|
||||
self._settings.set_persistent_setting(setting)
|
||||
|
||||
def generate_reverse_dependencies_metadata(self, repo = None):
|
||||
dbconn = self.open_server_repository(read_only = False,
|
||||
no_upload = True, repo = repo)
|
||||
dbconn.generateReverseDependenciesMetadata()
|
||||
self._taint_database(repo = repo)
|
||||
dbconn.commitChanges()
|
||||
|
||||
def _get_gpg_signature(self, repo_sec, repo, pkg_path):
|
||||
try:
|
||||
if not repo_sec.is_keypair_available(repo):
|
||||
|
||||
Reference in New Issue
Block a user