]>
Commit | Line | Data |
---|---|---|
47a4cb89 MT |
1 | #!/usr/bin/python |
2 | ||
3 | import logging | |
4 | import os | |
2568a6d1 | 5 | import shutil |
47a4cb89 | 6 | import sqlite3 |
66af936c | 7 | import time |
47a4cb89 | 8 | |
a2d1644c | 9 | from pakfire.constants import * |
66af936c | 10 | |
b6da0663 MT |
11 | class Cursor(sqlite3.Cursor): |
12 | def execute(self, *args, **kwargs): | |
13 | # For debugging of SQL queries. | |
14 | #print args, kwargs | |
15 | ||
16 | return sqlite3.Cursor.execute(self, *args, **kwargs) | |
17 | ||
18 | ||
47a4cb89 | 19 | class Database(object): |
3723913b MT |
20 | def __init__(self, pakfire, filename): |
21 | self.pakfire = pakfire | |
47a4cb89 MT |
22 | self.filename = filename |
23 | self._db = None | |
24 | ||
25 | self.open() | |
26 | ||
27 | def __del__(self): | |
28 | if self._db: | |
fa6d335b | 29 | #self._db.commit() |
47a4cb89 MT |
30 | self._db.close() |
31 | ||
32 | def create(self): | |
33 | pass | |
34 | ||
35 | def open(self): | |
36 | if not self._db: | |
37 | logging.debug("Open database %s" % self.filename) | |
38 | ||
a2d1644c MT |
39 | database_exists = False |
40 | ||
41 | if not self.filename == ":memory:": | |
42 | dirname = os.path.dirname(self.filename) | |
43 | if not os.path.exists(dirname): | |
44 | os.makedirs(dirname) | |
47a4cb89 | 45 | |
a2d1644c | 46 | database_exists = os.path.exists(self.filename) |
47a4cb89 MT |
47 | |
48 | # Make a connection to the database. | |
49 | self._db = sqlite3.connect(self.filename) | |
50 | self._db.row_factory = sqlite3.Row | |
51 | ||
52 | # Create the database if it was not there, yet. | |
53 | if not database_exists: | |
54 | self.create() | |
55 | ||
56 | def close(self): | |
57 | self._db.close() | |
2568a6d1 | 58 | self._db = None |
47a4cb89 MT |
59 | |
60 | def commit(self): | |
61 | self._db.commit() | |
62 | ||
63 | def cursor(self): | |
b6da0663 | 64 | return self._db.cursor(Cursor) |
47a4cb89 | 65 | |
a2d1644c MT |
66 | def executescript(self, *args, **kwargs): |
67 | return self._db.executescript(*args, **kwargs) | |
68 | ||
2568a6d1 MT |
69 | def save(self, path): |
70 | """ | |
a2d1644c | 71 | Save a copy of this database to a new one located at path. |
2568a6d1 | 72 | """ |
a2d1644c MT |
73 | db2 = Database(self.pakfire, path) |
74 | ||
75 | script = "" | |
76 | for line in self._db.iterdump(): | |
77 | script += "%s\n" % line | |
78 | ||
79 | db2.executescript(script) | |
80 | db2.commit() | |
2568a6d1 | 81 | |
a2d1644c | 82 | db2.close() |
2568a6d1 | 83 | |
47a4cb89 | 84 | |
fa6d335b | 85 | class PackageDatabase(Database): |
47a4cb89 MT |
86 | def create(self): |
87 | c = self.cursor() | |
88 | ||
89 | c.executescript(""" | |
90 | CREATE TABLE files( | |
91 | name TEXT, | |
92 | pkg INTEGER, | |
93 | size INTEGER, | |
94 | type INTEGER, | |
66af936c | 95 | hash1 TEXT |
47a4cb89 MT |
96 | ); |
97 | ||
98 | CREATE TABLE packages( | |
99 | id INTEGER PRIMARY KEY, | |
100 | name TEXT, | |
101 | epoch INTEGER, | |
102 | version TEXT, | |
103 | release TEXT, | |
c560c27a | 104 | arch TEXT, |
fa6d335b | 105 | filename TEXT, |
ba8c383d | 106 | size INTEGER, |
47a4cb89 MT |
107 | hash1 TEXT, |
108 | provides TEXT, | |
109 | requires TEXT, | |
110 | conflicts TEXT, | |
111 | obsoletes TEXT, | |
112 | license TEXT, | |
113 | summary TEXT, | |
114 | description TEXT, | |
1317485d | 115 | uuid TEXT, |
47a4cb89 MT |
116 | build_id TEXT, |
117 | build_host TEXT, | |
ba8c383d | 118 | build_date INTEGER |
47a4cb89 MT |
119 | ); |
120 | """) | |
121 | # XXX add some indexes here | |
122 | ||
123 | self.commit() | |
124 | c.close() | |
125 | ||
126 | def list_packages(self): | |
127 | c = self.cursor() | |
128 | c.execute("SELECT DISTINCT name FROM packages ORDER BY name") | |
129 | ||
130 | for pkg in c: | |
131 | yield pkg["name"] | |
132 | ||
133 | c.close() | |
134 | ||
fa6d335b MT |
135 | def package_exists(self, pkg): |
136 | return not self.get_id_by_pkg(pkg) is None | |
137 | ||
138 | def get_id_by_pkg(self, pkg): | |
139 | c = self.cursor() | |
140 | ||
141 | c.execute("SELECT id FROM packages WHERE name = ? AND version = ? AND \ | |
142 | release = ? AND epoch = ? LIMIT 1", (pkg.name, pkg.version, pkg.release, pkg.epoch)) | |
143 | ||
144 | ret = None | |
145 | for i in c: | |
146 | ret = i["id"] | |
147 | break | |
66af936c | 148 | |
fa6d335b MT |
149 | c.close() |
150 | ||
151 | return ret | |
152 | ||
153 | def add_package(self, pkg): | |
66af936c MT |
154 | raise NotImplementedError |
155 | ||
156 | ||
157 | class RemotePackageDatabase(PackageDatabase): | |
158 | def add_package(self, pkg, reason=None): | |
fa6d335b MT |
159 | if self.package_exists(pkg): |
160 | logging.debug("Skipping package which already exists in database: %s" % pkg.friendly_name) | |
161 | return | |
162 | ||
163 | logging.debug("Adding package to database: %s" % pkg.friendly_name) | |
164 | ||
66af936c MT |
165 | filename = "" |
166 | if pkg.repo.local: | |
ab86bec4 MT |
167 | # Get the path relatively to the repository. |
168 | filename = pkg.filename[len(pkg.repo.path):] | |
169 | # Strip leading / if any. | |
170 | if filename.startswith("/"): | |
171 | filename = filename[1:] | |
66af936c | 172 | |
fa6d335b MT |
173 | c = self.cursor() |
174 | c.execute(""" | |
175 | INSERT INTO packages( | |
176 | name, | |
177 | epoch, | |
178 | version, | |
179 | release, | |
c560c27a | 180 | arch, |
fa6d335b | 181 | filename, |
e399ad3a | 182 | size, |
66af936c | 183 | hash1, |
fa6d335b | 184 | provides, |
66af936c MT |
185 | requires, |
186 | conflicts, | |
187 | obsoletes, | |
188 | license, | |
189 | summary, | |
190 | description, | |
1317485d | 191 | uuid, |
66af936c MT |
192 | build_id, |
193 | build_host, | |
194 | build_date | |
1317485d | 195 | ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", |
fa6d335b MT |
196 | ( |
197 | pkg.name, | |
198 | pkg.epoch, | |
199 | pkg.version, | |
200 | pkg.release, | |
c560c27a | 201 | pkg.arch, |
66af936c | 202 | filename, |
e399ad3a | 203 | pkg.size, |
66af936c | 204 | pkg.hash1, |
fa6d335b MT |
205 | " ".join(pkg.provides), |
206 | " ".join(pkg.requires), | |
66af936c MT |
207 | " ".join(pkg.conflicts), |
208 | " ".join(pkg.obsoletes), | |
209 | pkg.license, | |
210 | pkg.summary, | |
211 | pkg.description, | |
1317485d | 212 | pkg.uuid, |
66af936c MT |
213 | pkg.build_id, |
214 | pkg.build_host, | |
215 | pkg.build_date | |
fa6d335b MT |
216 | ) |
217 | ) | |
fa6d335b | 218 | self.commit() |
66af936c | 219 | c.close() |
fa6d335b MT |
220 | |
221 | pkg_id = self.get_id_by_pkg(pkg) | |
222 | ||
223 | c = self.cursor() | |
224 | for file in pkg.filelist: | |
225 | c.execute("INSERT INTO files(name, pkg) VALUES(?, ?)", (file, pkg_id)) | |
226 | ||
66af936c | 227 | self.commit() |
fa6d335b | 228 | c.close() |
66af936c MT |
229 | |
230 | return pkg_id | |
231 | ||
232 | ||
233 | class LocalPackageDatabase(RemotePackageDatabase): | |
234 | def __init__(self, pakfire): | |
235 | # Generate filename for package database | |
236 | filename = os.path.join(pakfire.path, PACKAGES_DB) | |
237 | ||
238 | RemotePackageDatabase.__init__(self, pakfire, filename) | |
239 | ||
240 | def create(self): | |
241 | RemotePackageDatabase.create(self) | |
242 | ||
243 | # Alter the database layout to store additional local information. | |
244 | logging.debug("Altering database table for local information.") | |
245 | c = self.cursor() | |
246 | c.executescript(""" | |
247 | ALTER TABLE packages ADD COLUMN installed INT; | |
248 | ALTER TABLE packages ADD COLUMN reason TEXT; | |
249 | ALTER TABLE packages ADD COLUMN repository TEXT; | |
a5f5fced | 250 | ALTER TABLE packages ADD COLUMN scriptlet TEXT; |
9c75e8ed | 251 | ALTER TABLE packages ADD COLUMN triggers TEXT; |
66af936c | 252 | """) |
fa6d335b | 253 | self.commit() |
66af936c | 254 | c.close() |
fa6d335b | 255 | |
66af936c MT |
256 | def add_package(self, pkg, reason=None): |
257 | # Insert all the information to the database we have in the remote database | |
258 | pkg_id = RemotePackageDatabase.add_package(self, pkg) | |
fa6d335b | 259 | |
66af936c | 260 | # then: add some more information |
47a4cb89 MT |
261 | c = self.cursor() |
262 | ||
66af936c MT |
263 | # Save timestamp when the package was installed. |
264 | c.execute("UPDATE packages SET installed = ? WHERE id = ?", (time.time(), pkg_id)) | |
47a4cb89 | 265 | |
66af936c MT |
266 | # Add repository information. |
267 | c.execute("UPDATE packages SET repository = ? WHERE id = ?", (pkg.repo.name, pkg_id)) | |
47a4cb89 | 268 | |
66af936c MT |
269 | # Save reason of installation (if any). |
270 | if reason: | |
271 | c.execute("UPDATE packages SET reason = ? WHERE id = ?", (reason, pkg_id)) | |
47a4cb89 | 272 | |
66af936c MT |
273 | # Update the filename information. |
274 | c.execute("UPDATE packages SET filename = ? WHERE id = ?", (pkg.filename, pkg_id)) | |
47a4cb89 | 275 | |
a5f5fced MT |
276 | # Add the scriptlet to database (needed to update or uninstall packages). |
277 | c.execute("UPDATE packages SET scriptlet = ? WHERE id = ?", (pkg.scriptlet, pkg_id)) | |
278 | ||
9c75e8ed MT |
279 | # Add triggers to the database. |
280 | triggers = " ".join(pkg.triggers) | |
281 | c.execute("UPDATE packages SET triggers = ? WHERE id = ?", (triggers, pkg_id)) | |
282 | ||
66af936c MT |
283 | self.commit() |
284 | c.close() |