aboutsummaryrefslogtreecommitdiff
blob: cfa0051e6647fe2956203da083ad3734f3a3513d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Copyright 1999-2010 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

from portage.cache import template
from portage.cache.cache_errors import CacheCorruption
from portage.cache.flat_hash import database as db_rw
from portage.cache.metadata import database as db_ro

class database(template.database):

	serialize_eclasses = False

	def __init__(self, location, label, auxdbkeys, db_rw=db_rw, db_ro=db_ro,
		*args, **config):
		super_config = config.copy()
		super_config.pop("gid", None)
		super_config.pop("perms", None)
		super(database, self).__init__(location, label, auxdbkeys,
			*args, **super_config)
		self.db_rw = db_rw(location, label, auxdbkeys, **config)
		self.commit = self.db_rw.commit
		self.autocommits = self.db_rw.autocommits
		if isinstance(db_ro, type):
			ro_config = config.copy()
			ro_config["readonly"] = True
			self.db_ro = db_ro(label, "metadata/cache", auxdbkeys, **ro_config)
		else:
			self.db_ro = db_ro

	def __getitem__(self, cpv):
		"""funnel whiteout validation through here, since value needs to be fetched"""
		try:
			value = self.db_rw[cpv]
		except KeyError:
			return self.db_ro[cpv] # raises a KeyError when necessary
		except CacheCorruption:
			del self.db_rw[cpv]
			return self.db_ro[cpv] # raises a KeyError when necessary
		if self._is_whiteout(value):
			if self._is_whiteout_valid(cpv, value):
				raise KeyError(cpv)
			else:
				del self.db_rw[cpv]
				return self.db_ro[cpv] # raises a KeyError when necessary
		else:
			return value

	def _setitem(self, name, values):
		try:
			value_ro = self.db_ro.get(name)
		except CacheCorruption:
			value_ro = None
		if value_ro is not None and \
			self._are_values_identical(value_ro, values):
			# we have matching values in the underlying db_ro
			# so it is unnecessary to store data in db_rw
			try:
				del self.db_rw[name] # delete unwanted whiteout when necessary
			except KeyError:
				pass
			return
		self.db_rw[name] = values

	def _delitem(self, cpv):
		value = self[cpv] # validates whiteout and/or raises a KeyError when necessary
		if cpv in self.db_ro:
			self.db_rw[cpv] = self._create_whiteout(value)
		else:
			del self.db_rw[cpv]

	def __contains__(self, cpv):
		try:
			self[cpv] # validates whiteout when necessary
		except KeyError:
			return False
		return True

	def __iter__(self):
		s = set()
		for cpv in self.db_rw:
			if cpv in self: # validates whiteout when necessary
				yield cpv
			# set includes whiteouts so they won't be yielded later
			s.add(cpv)
		for cpv in self.db_ro:
			if cpv not in s:
				yield cpv

	def _is_whiteout(self, value):
		return value["EAPI"] == "whiteout"

	def _create_whiteout(self, value):
		return {"EAPI":"whiteout","_eclasses_":value["_eclasses_"],"_mtime_":value["_mtime_"]}

	def _is_whiteout_valid(self, name, value_rw):
		try:
			value_ro = self.db_ro[name]
			return self._are_values_identical(value_rw,value_ro)
		except KeyError:
			return False

	def _are_values_identical(self, value1, value2):
		if value1['_mtime_'] != value2['_mtime_']:
			return False
		return value1["_eclasses_"] == value2["_eclasses_"]