]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/buildservice/packages.py
Fix ambiguity in SQL statement
[people/jschlag/pbs.git] / src / buildservice / packages.py
CommitLineData
9137135a
MT
1#!/usr/bin/python
2
f6e6ff79
MT
3import datetime
4import logging
9137135a
MT
5import os
6import shutil
9137135a 7
f6e6ff79
MT
8import pakfire
9import pakfire.packages as packages
10
2c909128
MT
11from . import arches
12from . import base
13from . import database
14from . import misc
15from . import sources
9137135a 16
2c909128 17from .constants import *
9137135a
MT
18
19class Packages(base.Object):
f6e6ff79 20 def get_all_names(self, public=None, user=None, states=None):
3a0302c1 21 query = "SELECT DISTINCT packages.name AS name, summary FROM packages \
f6e6ff79
MT
22 JOIN builds ON builds.pkg_id = packages.id \
23 WHERE packages.type = 'source'"
24
25 conditions = []
26 args = []
27
28 if public in (True, False):
29 if public is True:
30 public = "Y"
31 elif public is False:
32 public = "N"
33
34 conditions.append("builds.public = %s")
35 args.append(public)
36
37 if user and not user.is_admin():
38 conditions.append("builds.owner_id = %s")
39 args.append(user.id)
40
41 if states:
42 for state in states:
43 conditions.append("builds.state = %s")
44 args.append(state)
45
46 if conditions:
47 query += " AND (%s)" % " OR ".join(conditions)
48
49 query += " ORDER BY packages.name"
50
51 return [(n.name, n.summary) for n in self.db.query(query, *args)]
52
53 def get_by_uuid(self, uuid):
1d2a49fe 54 pkg = self.db.get("SELECT * FROM packages WHERE uuid = %s LIMIT 1", uuid)
f6e6ff79
MT
55 if not pkg:
56 return
57
1d2a49fe 58 return Package(self.pakfire, pkg.id, pkg)
f6e6ff79
MT
59
60 def search(self, pattern, limit=None):
61 """
62 Searches for packages that do match the query.
63
64 This function does not work for UUIDs or filenames.
65 """
ce7abd33
MT
66 query = "SELECT * FROM packages \
67 WHERE type = %s AND ( \
68 name LIKE %s OR \
69 summary LIKE %s OR \
70 description LIKE %s \
71 ) \
f6e6ff79
MT
72 GROUP BY name"
73
ce7abd33
MT
74 pattern = "%%%s%%" % pattern
75 args = ("source", pattern, pattern, pattern)
76
77 res = self.db.query(query, *args)
78
f6e6ff79 79 pkgs = []
ce7abd33
MT
80 for row in res:
81 pkg = Package(self.pakfire, row.id, row)
f6e6ff79
MT
82 pkgs.append(pkg)
83
84 if limit and len(pkgs) >= limit:
85 break
9137135a 86
f6e6ff79 87 return pkgs
9137135a 88
f6e6ff79
MT
89 def search_by_filename(self, filename, limit=None):
90 query = "SELECT filelists.* FROM filelists \
91 JOIN packages ON filelists.pkg_id = packages.id \
92 WHERE filelists.name = %s ORDER BY packages.build_time DESC"
93 args = [filename,]
9137135a 94
f6e6ff79
MT
95 if limit:
96 query += " LIMIT %s"
97 args.append(limit)
9137135a 98
f6e6ff79
MT
99 files = []
100 for result in self.db.query(query, *args):
101 pkg = Package(self.pakfire, result.pkg_id)
102 files.append((pkg, result))
9137135a 103
f6e6ff79 104 return files
9137135a 105
35c46db4
MT
106 def autocomplete(self, query, limit=8):
107 res = self.db.query("SELECT DISTINCT name FROM packages \
108 WHERE packages.name LIKE %s AND packages.type = %s \
109 ORDER BY packages.name LIMIT %s", "%%%s%%" % query, "source", limit)
110
111 return [row.name for row in res]
112
9137135a 113
f6e6ff79 114class Package(base.Object):
1d2a49fe 115 def __init__(self, pakfire, id, data=None):
f6e6ff79 116 base.Object.__init__(self, pakfire)
9137135a 117
f6e6ff79
MT
118 # The ID of the package.
119 self.id = id
9137135a 120
f6e6ff79 121 # Cache.
1d2a49fe 122 self._data = data
f6e6ff79
MT
123 self._deps = None
124 self._arch = None
125 self._filelist = None
126 self._job = None
127 self._commit = None
128 self._properties = None
129 self._maintainer = None
9137135a 130
f6e6ff79
MT
131 def __repr__(self):
132 return "<%s %s>" % (self.__class__.__name__, self.friendly_name)
9137135a 133
f6e6ff79
MT
134 def __cmp__(self, other):
135 return pakfire.util.version_compare(self.pakfire,
136 self.friendly_name, other.friendly_name)
9137135a 137
f6e6ff79 138 @classmethod
6c439abc 139 def open(cls, _pakfire, path):
f6e6ff79
MT
140 # Just check if the file really does exist.
141 assert os.path.exists(path)
142
6c439abc
MT
143 p = pakfire.PakfireServer()
144 file = packages.open(p, None, path)
f6e6ff79
MT
145
146 # Get architecture from the database.
6c439abc 147 arch = _pakfire.arches.get_by_name(file.arch)
f6e6ff79
MT
148 assert arch, "Unknown architecture: %s" % file.arch
149
150 hash_sha512 = misc.calc_hash(path, "sha512")
151 assert hash_sha512
152
153 query = [
154 ("name", file.name),
155 ("epoch", file.epoch),
156 ("version", file.version),
157 ("release", file.release),
158 ("type", file.type),
159 ("arch", arch.id),
160
161 ("groups", " ".join(file.groups)),
162 ("maintainer", file.maintainer),
163 ("license", file.license),
164 ("url", file.url),
165 ("summary", file.summary),
166 ("description", file.description),
7a59e9ba 167 ("size", file.inst_size),
f6e6ff79
MT
168 ("uuid", file.uuid),
169
170 # Build information.
171 ("build_id", file.build_id),
172 ("build_host", file.build_host),
173 ("build_time", datetime.datetime.utcfromtimestamp(file.build_time)),
174
175 # File "metadata".
176 ("path", path),
177 ("filesize", os.path.getsize(path)),
178 ("hash_sha512", hash_sha512),
179 ]
180
181 if file.type == "source":
182 query.append(("supported_arches", file.supported_arches))
183
184 keys = []
185 vals = []
186 for key, val in query:
187 keys.append("`%s`" % key)
188 vals.append(val)
189
190 _query = "INSERT INTO packages(%s)" % ", ".join(keys)
191 _query += " VALUES(%s)" % ", ".join("%s" for v in vals)
192
193 # Create package entry in the database.
6c439abc 194 id = _pakfire.db.execute(_query, *vals)
f6e6ff79
MT
195
196 # Dependency information.
197 deps = []
198 for d in file.prerequires:
199 deps.append((id, "prerequires", d))
200
201 for d in file.requires:
202 deps.append((id, "requires", d))
203
204 for d in file.provides:
205 deps.append((id, "provides", d))
206
207 for d in file.conflicts:
208 deps.append((id, "conflicts", d))
209
210 for d in file.obsoletes:
211 deps.append((id, "obsoletes", d))
212
213 if deps:
6c439abc 214 _pakfire.db.executemany("INSERT INTO packages_deps(pkg_id, type, what) \
f6e6ff79
MT
215 VALUES(%s, %s, %s)", deps)
216
217 # Add all files to filelists table.
218 filelist = []
219 for f in file.filelist:
220 if f.config:
221 config = "Y"
222 else:
223 config = "N"
9137135a 224
f6e6ff79
MT
225 # Convert mtime to integer.
226 try:
227 mtime = int(f.mtime)
228 except ValueError:
229 mtime = 0
9137135a 230
f6e6ff79
MT
231 filelist.append((id, f.name, f.size, f.hash1, f.type, config, f.mode,
232 f.user, f.group, datetime.datetime.utcfromtimestamp(mtime),
233 f.capabilities))
9137135a 234
6c439abc 235 _pakfire.db.executemany("INSERT INTO filelists(pkg_id, name, size, hash_sha512, \
f6e6ff79
MT
236 type, config, mode, user, `group`, mtime, capabilities) \
237 VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", filelist)
9137135a 238
f6e6ff79 239 # Return the newly created object.
6c439abc 240 return cls(_pakfire, id)
9137135a 241
f6e6ff79
MT
242 def delete(self):
243 self.db.execute("INSERT INTO queue_delete(path) VALUES(%s)", self.path)
9137135a 244
f6e6ff79
MT
245 # Delete all files from the filelist.
246 self.db.execute("DELETE FROM filelists WHERE pkg_id = %s", self.id)
9137135a 247
f6e6ff79
MT
248 # Delete the package.
249 self.db.execute("DELETE FROM packages WHERE id = %s", self.id)
9137135a 250
f6e6ff79
MT
251 # Remove cached data.
252 self._data = {}
9137135a 253
f6e6ff79
MT
254 @property
255 def data(self):
256 if self._data is None:
257 self._data = \
258 self.db.get("SELECT * FROM packages WHERE id = %s", self.id)
259 assert self._data, "Cannot fetch package %s: %s" % (self.id, self._data)
9137135a 260
f6e6ff79 261 return self._data
9137135a 262
f6e6ff79
MT
263 @property
264 def uuid(self):
265 return self.data.uuid
9137135a
MT
266
267 @property
268 def name(self):
f6e6ff79 269 return self.data.name
9137135a
MT
270
271 @property
272 def epoch(self):
f6e6ff79 273 return self.data.epoch
9137135a
MT
274
275 @property
276 def version(self):
f6e6ff79 277 return self.data.version
9137135a
MT
278
279 @property
280 def release(self):
f6e6ff79
MT
281 return self.data.release
282
283 @property
284 def arch(self):
285 if self._arch is None:
286 self._arch = self.pakfire.arches.get_by_id(self.data.arch)
287 assert self._arch
288
289 return self._arch
290
291 @property
292 def type(self):
293 return self.data.type
9137135a
MT
294
295 @property
296 def friendly_name(self):
f6e6ff79 297 return "%s-%s.%s" % (self.name, self.friendly_version, self.arch.name)
9137135a
MT
298
299 @property
300 def friendly_version(self):
301 s = "%s-%s" % (self.version, self.release)
302
303 if self.epoch:
304 s = "%d:%s" % (self.epoch, s)
305
306 return s
307
308 @property
f6e6ff79
MT
309 def groups(self):
310 return self.data.groups.split()
9137135a 311
f6e6ff79
MT
312 @property
313 def maintainer(self):
314 if self._maintainer is None:
315 self._maintainer = self.data.maintainer
9137135a 316
f6e6ff79
MT
317 # Search if there is a user account for this person.
318 user = self.pakfire.users.find_maintainer(self._maintainer)
319 if user:
320 self._maintainer = user
9137135a 321
f6e6ff79 322 return self._maintainer
9137135a
MT
323
324 @property
f6e6ff79
MT
325 def license(self):
326 return self.data.license
9137135a
MT
327
328 @property
f6e6ff79
MT
329 def url(self):
330 return self.data.url
9137135a
MT
331
332 @property
f6e6ff79
MT
333 def summary(self):
334 return self.data.summary
9137135a
MT
335
336 @property
f6e6ff79
MT
337 def description(self):
338 return self.data.description
9137135a
MT
339
340 @property
f6e6ff79
MT
341 def supported_arches(self):
342 return self.data.supported_arches
9137135a
MT
343
344 @property
f6e6ff79
MT
345 def size(self):
346 return self.data.size
9137135a 347
5669a87f
MT
348 def has_deps(self):
349 """
350 Returns True if the package has got dependencies.
351
352 Always filter out the uuid provides.
353 """
354 return len(self.deps) > 1
355
9137135a 356 @property
f6e6ff79
MT
357 def deps(self):
358 if self._deps is None:
359 query = self.db.query("SELECT type, what FROM packages_deps WHERE pkg_id = %s", self.id)
9137135a 360
f6e6ff79
MT
361 self._deps = []
362 for row in query:
363 self._deps.append((row.type, row.what))
9137135a 364
f6e6ff79 365 return self._deps
9137135a
MT
366
367 @property
f6e6ff79
MT
368 def prerequires(self):
369 return [d[1] for d in self.deps if d[0] == "prerequires"]
9137135a
MT
370
371 @property
f6e6ff79
MT
372 def requires(self):
373 return [d[1] for d in self.deps if d[0] == "requires"]
9137135a
MT
374
375 @property
f6e6ff79 376 def provides(self):
5669a87f 377 return [d[1] for d in self.deps if d[0] == "provides" and not d[1].startswith("uuid(")]
9137135a
MT
378
379 @property
f6e6ff79
MT
380 def conflicts(self):
381 return [d[1] for d in self.deps if d[0] == "conflicts"]
9137135a 382
f6e6ff79
MT
383 @property
384 def obsoletes(self):
385 return [d[1] for d in self.deps if d[0] == "obsoletes"]
9137135a 386
5669a87f
MT
387 @property
388 def suggests(self):
389 return [d[1] for d in self.deps if d[0] == "suggests"]
390
391 @property
392 def recommends(self):
393 return [d[1] for d in self.deps if d[0] == "recommends"]
394
f6e6ff79
MT
395 @property
396 def commit_id(self):
397 return self.data.commit_id
9137135a 398
f6e6ff79
MT
399 def get_commit(self):
400 if not self.commit_id:
401 return
9137135a 402
f6e6ff79
MT
403 if self._commit is None:
404 self._commit = sources.Commit(self.pakfire, self.commit_id)
9137135a 405
f6e6ff79 406 return self._commit
9137135a 407
f6e6ff79
MT
408 def set_commit(self, commit):
409 self.db.execute("UPDATE packages SET commit_id = %s WHERE id = %s",
410 commit.id, self.id)
411 self._commit = commit
9137135a 412
f6e6ff79 413 commit = property(get_commit, set_commit)
9137135a 414
f6e6ff79
MT
415 @property
416 def distro(self):
417 if not self.commit:
418 return
9137135a 419
f6e6ff79 420 # XXX THIS CANNOT RETURN None
9137135a 421
f6e6ff79 422 return self.commit.distro
9137135a 423
f6e6ff79
MT
424 @property
425 def build_id(self):
426 return self.data.build_id
9137135a
MT
427
428 @property
f6e6ff79
MT
429 def build_host(self):
430 return self.data.build_host
9137135a 431
f6e6ff79
MT
432 @property
433 def build_time(self):
434 return self.data.build_time
9137135a 435
f6e6ff79
MT
436 @property
437 def path(self):
438 return self.data.path
9137135a 439
f6e6ff79
MT
440 @property
441 def hash_sha512(self):
442 return self.data.hash_sha512
9137135a 443
f6e6ff79
MT
444 @property
445 def filesize(self):
446 return self.data.filesize
9137135a 447
f6e6ff79
MT
448 def move(self, target_dir):
449 # Create directory if it does not exist, yet.
450 if not os.path.exists(target_dir):
451 os.makedirs(target_dir)
9137135a 452
f6e6ff79
MT
453 # Make full path where to put the file.
454 target = os.path.join(target_dir, os.path.basename(self.path))
9137135a 455
f6e6ff79
MT
456 # Copy the file to the target directory (keeping metadata).
457 shutil.move(self.path, target)
9137135a 458
f6e6ff79
MT
459 # Update file path in the database.
460 self.db.execute("UPDATE packages SET path = %s WHERE id = %s",
461 os.path.relpath(target, PACKAGES_DIR), self.id)
462 self._data["path"] = target
9137135a 463
f6e6ff79
MT
464 @property
465 def build(self):
80ee2515
MT
466 if self.job:
467 return self.job.build
468
f6e6ff79
MT
469 build = self.db.get("SELECT id FROM builds \
470 WHERE type = 'release' AND pkg_id = %s", self.id)
9137135a 471
f6e6ff79 472 if build:
2c909128 473 return self.pakfire.builds.get_by_id(build.id)
9137135a 474
f6e6ff79
MT
475 @property
476 def job(self):
477 if self._job is None:
478 job = self.db.get("SELECT job_id AS id FROM jobs_packages \
479 WHERE pkg_id = %s", self.id)
9137135a 480
f6e6ff79 481 if job:
2c909128 482 self._job = self.pakfire.jobs.get_by_id(job.id)
9137135a 483
f6e6ff79 484 return self._job
9137135a
MT
485
486 @property
f6e6ff79
MT
487 def filelist(self):
488 if self._filelist is None:
01197c1d
MT
489 self._filelist = []
490
491 for f in self.db.query("SELECT * FROM filelists WHERE pkg_id = %s ORDER BY name", self.id):
492 f = File(self.pakfire, f)
493 self._filelist.append(f)
9137135a 494
f6e6ff79 495 return self._filelist
9137135a 496
f6e6ff79 497 def get_file(self):
01197c1d
MT
498 path = os.path.join(PACKAGES_DIR, self.path)
499
500 if os.path.exists(path):
501 return pakfire.packages.open(None, None, path)
9137135a 502
f6e6ff79
MT
503 ## properties
504
505 _default_properties = {
506 "critical_path" : False,
507 "priority" : 0,
508 }
9137135a 509
f6e6ff79
MT
510 def update_property(self, key, value):
511 assert self._default_properties.has_key(key), "Unknown key: %s" % key
9137135a 512
f6e6ff79 513 #print self.db.execute("UPDATE packages_properties SET
9137135a
MT
514
515 @property
f6e6ff79
MT
516 def properties(self):
517 if self._properties is None:
518 self._properties = \
519 self.db.get("SELECT * FROM packages_properties WHERE name = %s", self.name)
9137135a 520
f6e6ff79
MT
521 if not self._properties:
522 self._properties = database.Row(self._default_properties)
9137135a 523
f6e6ff79 524 return self._properties
9137135a
MT
525
526 @property
f6e6ff79
MT
527 def critical_path(self):
528 return self.properties.get("critical_path", "N") == "Y"
9137135a 529
9137135a 530
01197c1d
MT
531class File(base.Object):
532 def __init__(self, pakfire, data):
533 base.Object.__init__(self, pakfire)
534
535 self.data = data
536
537 def __getattr__(self, attr):
538 try:
539 return self.data[attr]
540 except KeyError:
541 raise AttributeError, attr
542
543 @property
544 def downloadable(self):
545 # All regular files are downloadable.
546 return self.type == 0
547
548 @property
549 def viewable(self):
5669a87f
MT
550 # Empty files cannot be viewed.
551 if self.size == 0:
552 return False
553
01197c1d
MT
554 for ext in FILE_EXTENSIONS_VIEWABLE:
555 if self.name.endswith(ext):
556 return True
557
558 return False