]> git.decadent.org.uk Git - dak.git/blob - daklib/dbconn.py
add very basic set of ORM classes
[dak.git] / daklib / dbconn.py
1 #!/usr/bin/python
2
3 """ DB access class
4
5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006  James Troup <james@nocrew.org>
7 @copyright: 2008-2009  Mark Hymers <mhy@debian.org>
8 @copyright: 2009  Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009  Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
11 """
12
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
17
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
27 ################################################################################
28
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 #  * mhy looks up the definition of "funny"
33
34 ################################################################################
35
36 import os
37 import psycopg2
38 import traceback
39
40 from sqlalchemy import create_engine, Table, MetaData, select
41 from sqlalchemy.orm import sessionmaker, mapper
42
43 from singleton import Singleton
44 from config import Config
45
46 ################################################################################
47
48 class Cache(object):
49     def __init__(self, hashfunc=None):
50         if hashfunc:
51             self.hashfunc = hashfunc
52         else:
53             self.hashfunc = lambda x: str(x)
54
55         self.data = {}
56
57     def SetValue(self, keys, value):
58         self.data[self.hashfunc(keys)] = value
59
60     def GetValue(self, keys):
61         return self.data.get(self.hashfunc(keys))
62
63 ################################################################################
64
65 class Architecture(object):
66     def __init__(self, arch_id=None, arch_string=None, description=None):
67         self.arch_id = arch_id
68         self.arch_string = arch_string
69         self.description = description
70
71     def __repr__(self):
72         return '<Architecture %s>' % self.arch_string
73
74 class Archive(object):
75     def __init__(self, archive_id=None, archive_name=None, origin_server=None,
76                  description=None):
77         self.archive_id = archive_id
78         self.archive_name = archive_name
79         self.origin_server = origin_server
80         self.description = description
81
82     def __repr__(self):
83         return '<Archive %s>' % self.name
84
85 class BinAssociation(object):
86     def __init__(self, ba_id=None, suite_id=None, bin_id=None):
87         self.ba_id = ba_id
88         self.suite_id = suite_id
89         self.bin_id = bin_id
90
91     def __repr__(self):
92         return '<BinAssociation %s>' % self.ba_id
93
94 class Binary(object):
95     def __init__(self, binary_id=None, package=None, version=None,
96                  maintainer_id=None, source_id=None, arch_id=None,
97                  file_id=None, filetype=None, fingerprint_id=None,
98                  install_date=None):
99         self.binary_id = binary_id
100         self.package = package
101         self.version = version
102         self.maintainer_id = maintainer_id
103         self.source_id = source_id
104         self.arch_id = arch_id
105         self.file_id = file_id
106         self.filetype = filetype
107         self.fingerprint_id = fingerprint_id
108         self.install_date = install_date
109
110     def __repr__(self):
111         return '<Binary %s (%s) %s>' % (self.package, self.version, self.arch_id)
112
113 class Component(object):
114     def __init__(self, component_id=None, component_name=None,
115                  description=None, meets_dfsg=None,):
116         self.component_id = component_id
117         self.component_name = component_name
118         self.description = description
119         self.meets_dfsg = meets_dfsg
120
121     def __repr__(self):
122         return '<Component %s>' % self.component_name
123
124 class DBConfig(object):
125     def __init__(self, config_id=None, name=None, value=None):
126         self.config_id = config_id
127         self.name = name
128         self.value = value
129
130     def __repr__(self):
131         return '<DBConfig %s>' % self.name
132
133 class ContentFilename(object):
134     def __init__(self, cafilename_id=None, filename=None):
135         self.cafilename_id = cafilename_id
136         self.filename = filename
137
138     def __repr__(self):
139         return '<ContentFilename %s>' % self.filename
140
141 class ContentFilepath(object):
142     def __init__(self, cafilepath_id=None, filepath=None):
143         self.cafilepath_id = cafilepath_id
144         self.filepath = filepath
145
146     def __repr__(self):
147         return '<ContentFilepath %s>' % self.filepath
148
149 class ContentAssociations(object):
150     def __init__(self, binary_id=None, filename_id=None, filepath_id=None,
151                  ca_id=None):
152         self.binary_id = binary_id
153         self.filename_id = filename_id
154         self.filepath_id = filepath_id
155         self.ca_id = ca_id
156
157     def __repr__(self):
158         return '<ContentAssociation %s>' % self.ca_id
159
160 class DSCFile(object):
161     def __init__(self, dscfile_id=None, source_id=None, file_id=None):
162         self.dscfile_id = dscfile_id
163         self.source_id = source_id
164         self.file_id = file_id
165
166     def __repr__(self):
167         return '<DSCFile %s>' % self.dscfile_id
168
169 class PoolFile(object):
170     def __init__(self, file_id=None, filename=None, filesize=None,
171                  location_id=None, last_used=None, md5sum=None,
172                  sha1sum=None, sha256sum=None):
173         self.file_id = file_id
174         self.filename = filename
175         self.filesize = filesize
176         self.location_id = location_id
177         self.last_used = last_used
178         self.md5sum = md5sum
179         self.sha1sum = sha1sum
180         self.sha256sum = sha256sum
181
182     def __repr__(self):
183         return '<PoolFile %s>' % self.filename
184
185 class Fingerprint(object):
186     def __init__(self, fingerprint_id=None, fingerprint=None,
187                  uid_id=None, keyring_id=None):
188         self.fingerprint_id = fingerprint_id
189         self.fingerprint = fingerprint
190         self.uid_id = uid_id
191         self.keyring_id = keyring_id
192
193     def __repr__(self):
194         return '<Fingerprint %s>' % self.fingerprint
195
196 class Keyring(object):
197     def __init__(self, keyring_id=None, keyring_name=None,
198                  debian_maintainer=None):
199         self.keyring_id = keyring_id
200         self.keyring_name = keyring_name
201         self.debian_maintainer = debian_maintainer
202
203     def __repr__(self):
204         return '<Keyring %s>' % self.keyring_name
205
206 class Location(object):
207     def __init__(self, location_id=None, path=None,
208                  component_id=None, archive_id=None,
209                  archive_type=None):
210         self.location_id = location_id
211         self.path = path
212         self.component_id = component_id
213         self.archive_id = archive_id
214         self.archive_type = archive_type
215
216     def __repr__(self):
217         return '<Location %s (%s)>' % (self.path, self.location_id)
218
219 class Maintainer(object):
220     def __init__(self, maintainer_id=None, name=None):
221         self.maintainer_id = maintainer_id
222         self.name = name
223
224     def __repr__(self):
225         return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
226
227 class Override(object):
228     def __init__(self, package, suite_id=None, component_id=None,
229                  priority_id=None, section_id=None, overridetype_id=None,
230                  maintainer=None):
231         self.package = package
232         self.suite_id = suite_id
233         self.component_id = component_id
234         self.priority_id = priority_id
235         self.section_id = section_id
236         self.overridetype_id = overridetype_id
237         self.maintainer = maintainer
238
239     def __repr__(self):
240         return '<Override %s (%s)>' % (self.package, self.suite_id)
241
242 class OverrideType(object):
243     def __init__(self, overridetype_id=None, overridetype=None):
244         self.overridetype_id = overridetype_id
245         self.overridetype = overridetype
246
247     def __repr__(self):
248         return '<OverrideType %s>' % self.overridetype
249
250 class PendingContentAssociation(object):
251     def __init__(self, pca_id=None, package=None, version=None,
252                  filepath_id=None, filename_id=None):
253         self.pca_id = pca_id
254         self.package = package
255         self.version = version
256         self.filepath_id = filepath_id
257         self.filename_id = filename_id
258
259     def __repr__(self):
260         return '<PendingContentAssociation %s>' % self.pca_id
261
262 class Priority(object):
263     def __init__(self, priority_id=None, priority=None, level=None):
264         self.priority_id = priority_id
265         self.priority = priority
266         self.level = level
267
268     def __repr__(self):
269         return '<Priority %s (%s)>' % (self.priority, self.priority_id)
270
271 class Queue(object):
272     def __init__(self, queue_id=None, queue_name=None):
273         self.queue_id = queue_id
274         self.queue_name = queue_name
275
276     def __repr__(self):
277         return '<Queue %s>' % self.queue_name
278
279 class QueueBuild(object):
280     def __init__(self, suite_id=None, queue_id=None, filename=None,
281                  in_queue=None, last_used=None):
282         self.suite_id = suite_id
283         self.queue_id = queue_id
284         self.filename = filename
285         self.in_queue = in_queue
286         self.last_used = last_used
287
288     def __repr__(self):
289         return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
290
291 class Section(object):
292     def __init__(self, section_id=None, section=None):
293         self.section_id = section_id
294         self.section = section
295
296     def __repr__(self):
297         return '<Section %s>' % self.section
298
299 class Source(object):
300     def __init__(self, source_id=None, source=None, version=None,
301                  maintainer_id=None, file_id=None, fingerprint_id=None,
302                  install_date=None, changedby_id=None, dm_upload_allowed=None):
303         self.source_id = source_id
304         self.source = source
305         self.version = version
306         self.maintainer_id = maintainer_id
307         self.file_id = file_id
308         self.fingerprint_id = fingerprint_id
309         self.install_date = install_date
310         self.changedby_id = changedby_id
311         self.dm_upload_allowed = dm_upload_allowed
312
313     def __repr__(self):
314         return '<Source %s (%s)>' % (self.source, self.version)
315
316 class SrcAssociation(object):
317     def __init__(self, sa_id=None, suite_id=None, source_id=None):
318         self.sa_id = sa_id
319         self.suite_id = suite_id
320         self.source_id = source_id
321
322     def __repr__(self):
323         return '<SrcAssociation %s>' % self.sa_id
324
325 class SrcUploader(object):
326     def __init__(self, uploader_id=None, source_id=None, maintainer_id=None):
327         self.uploader_id = uploader_id
328         self.source_id = source_id
329         self.maintainer_id = maintainer_id
330
331     def __repr__(self):
332         return '<SrcUploader %s>' % self.uploader_id
333
334 class Suite(object):
335     def __init__(self, suite_id=None, suite_name=None, version=None,
336                  origin=None, label=None, policy_engine=None,
337                  description=None, untouchable=None, announce=None,
338                  codename=None, overridecodename=None, validtime=None,
339                  priority=None, notautomatic=None, copychanges=None,
340                  copydotdak=None, commentsdir=None, overridesuite=None,
341                  changelogbase=None):
342
343         self.suite_id = suite_id
344         self.suite_name = suite_name
345         self.version = version
346         self.origin = origin
347         self.label = label
348         self.policy_engine = policy_engine
349         self.description = description
350         self.untouchable = untouchable
351         self.announce = announce
352         self.codename = codename
353         self.overridecodename = overridecodename
354         self.validtime = validtime
355         self.priority = priority
356         self.notautomatic = notautomatic
357         self.copychanges = copychanges
358         self.copydotdak = copydotdak
359         self.commentsdir = commentsdir
360         self.overridesuite = overridesuite
361         self.changelogbase = changelogbase
362
363     def __repr__(self):
364         return '<Suite %s>' % self.suite_name
365
366 class SuiteArchitecture(object):
367     def __init__(self, suite_id=None, arch_id=None):
368         self.suite_id = suite_id
369         self.arch_id = arch_id
370
371     def __repr__(self):
372         return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
373
374 class Uid(object):
375     def __init__(self, uid_id=None, uid=None, name=None):
376         self.uid_id = uid_id
377         self.uid = uid
378         self.name = name
379
380     def __repr__(self):
381         return '<Uid %s (%s)>' % (self.uid, self.name)
382
383 ################################################################################
384
385 class DBConn(Singleton):
386     """
387     database module init.
388     """
389     def __init__(self, *args, **kwargs):
390         super(DBConn, self).__init__(*args, **kwargs)
391
392     def _startup(self, *args, **kwargs):
393         self.__createconn()
394         self.__init_caches()
395
396     def __setuptables(self):
397         self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
398         mapper(Architecture, self.tbl_architecture,
399                properties = dict(arch_id = self.tbl_architecture.c.id))
400
401         self.tbl_archive = Table('archive', self.db_meta, autoload=True)
402         mapper(Archive, self.tbl_archive,
403                properties = dict(archive_id = self.tbl_archive.c.id,
404                                  archive_name = self.tbl_archive.c.name))
405
406         self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
407         mapper(BinAssociation, self.tbl_bin_associations,
408                properties = dict(ba_id = self.tbl_bin_associations.c.id,
409                                  suite_id = self.tbl_bin_associations.c.suite,
410                                  bin_id = self.tbl_bin_associations.c.bin))
411
412         self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
413         mapper(Binary, self.tbl_binaries,
414                properties = dict(binary_id = self.tbl_binaries.c.id,
415                                  file_id = self.tbl_binaries.c.file,
416                                  filetype = self.tbl_binaries.c.type,
417                                  maintainer_id = self.tbl_binaries.c.maintainer,
418                                  source_id = self.tbl_binaries.c.source,
419                                  arch_id = self.tbl_binaries.c.architecture,
420                                  fingerprint_id = self.tbl_binaries.c.sig_fpr))
421
422         self.tbl_component = Table('component', self.db_meta, autoload=True)
423         mapper(Component, self.tbl_component,
424                properties = dict(component_id = self.tbl_component.c.id,
425                                  component_name = self.tbl_component.c.name))
426
427         self.tbl_config = Table('config', self.db_meta, autoload=True)
428         mapper(DBConfig, self.tbl_config,
429                properties = dict(config_id = self.tbl_config.c.id))
430
431         self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
432         mapper(ContentAssociations, self.tbl_content_associations,
433                properties = dict(ca_id = self.tbl_content_associations.c.id,
434                                  filename_id = self.tbl_content_associations.c.filename,
435                                  filepath_id = self.tbl_content_associations.c.filepath,
436                                  binary_id   = self.tbl_content_associations.c.binary_pkg))
437
438         self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
439         mapper(ContentFilename, self.tbl_content_file_names,
440                properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
441                                  filename = self.tbl_content_file_names.c.file))
442
443         self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
444         mapper(ContentFilepath, self.tbl_content_file_paths,
445                properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
446                                  filepath = self.tbl_content_file_paths.c.path))
447
448         self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
449         mapper(DSCFile, self.tbl_dsc_files,
450                properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
451                                  source_id = self.tbl_dsc_files.c.source,
452                                  file_id = self.tbl_dsc_files.c.file))
453
454         self.tbl_files = Table('files', self.db_meta, autoload=True)
455         mapper(PoolFile, self.tbl_files,
456                properties = dict(file_id = self.tbl_files.c.id,
457                                  filesize = self.tbl_files.c.size,
458                                  location_id = self.tbl_files.c.location))
459
460         self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
461         mapper(Fingerprint, self.tbl_fingerprint,
462                properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
463                                  uid_id = self.tbl_fingerprint.c.uid,
464                                  keyring_id = self.tbl_fingerprint.c.keyring))
465
466         self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
467         mapper(Keyring, self.tbl_keyrings,
468                properties = dict(keyring_name = self.tbl_keyrings.c.name,
469                                  keyring_id = self.tbl_keyrings.c.id))
470
471         self.tbl_location = Table('location', self.db_meta, autoload=True)
472         mapper(Location, self.tbl_location,
473                properties = dict(location_id = self.tbl_location.c.id,
474                                  component_id = self.tbl_location.c.component,
475                                  archive_id = self.tbl_location.c.archive,
476                                  archive_type = self.tbl_location.c.type))
477
478         self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
479         mapper(Maintainer, self.tbl_maintainer,
480                properties = dict(maintainer_id = self.tbl_maintainer.c.id))
481
482         self.tbl_override = Table('override', self.db_meta, autoload=True)
483         mapper(Override, self.tbl_override,
484                properties = dict(suite_id = self.tbl_override.c.suite,
485                                  component_id = self.tbl_override.c.component,
486                                  priority_id = self.tbl_override.c.priority,
487                                  section_id = self.tbl_override.c.section,
488                                  overridetype_id = self.tbl_override.c.type))
489
490         self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
491         mapper(OverrideType, self.tbl_override_type,
492                properties = dict(overridetype = self.tbl_override_type.c.type,
493                                  overridetype_id = self.tbl_override_type.c.id))
494
495         self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
496         mapper(PendingContentAssociation, self.tbl_pending_content_associations,
497                properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
498                                  filepath_id = self.tbl_pending_content_associations.c.filepath,
499                                  filename_id = self.tbl_pending_content_associations.c.filename))
500
501         self.tbl_priority = Table('priority', self.db_meta, autoload=True)
502         mapper(Priority, self.tbl_priority,
503                properties = dict(priority_id = self.tbl_priority.c.id))
504
505         self.tbl_queue = Table('queue', self.db_meta, autoload=True)
506         mapper(Queue, self.tbl_queue,
507                properties = dict(queue_id = self.tbl_queue.c.id))
508
509         self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
510         mapper(QueueBuild, self.tbl_queue_build,
511                properties = dict(suite_id = self.tbl_queue_build.c.suite,
512                                  queue_id = self.tbl_queue_build.c.queue))
513
514         self.tbl_section = Table('section', self.db_meta, autoload=True)
515         mapper(Section, self.tbl_section,
516                properties = dict(section_id = self.tbl_section.c.id))
517
518         self.tbl_source = Table('source', self.db_meta, autoload=True)
519         mapper(Source, self.tbl_source,
520                properties = dict(source_id = self.tbl_source.c.id,
521                                  maintainer_id = self.tbl_source.c.maintainer,
522                                  file_id = self.tbl_source.c.file,
523                                  fingerprint_id = self.tbl_source.c.sig_fpr,
524                                  changedby_id = self.tbl_source.c.changedby))
525
526         self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
527         mapper(SrcAssociation, self.tbl_src_associations,
528                properties = dict(sa_id = self.tbl_src_associations.c.id))
529
530         self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
531         mapper(SrcUploader, self.tbl_src_uploaders,
532                properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
533                                  source_id = self.tbl_src_uploaders.c.source,
534                                  maintainer_id = self.tbl_src_uploaders.c.maintainer))
535
536         self.tbl_suite = Table('suite', self.db_meta, autoload=True)
537         mapper(Suite, self.tbl_suite,
538                properties = dict(suite_id = self.tbl_suite.c.id))
539
540         self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
541         mapper(SuiteArchitecture, self.tbl_suite_architectures,
542                properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
543                                  arch_id = self.tbl_suite_architectures.c.architecture))
544
545         self.tbl_uid = Table('uid', self.db_meta, autoload=True)
546         mapper(Uid, self.tbl_uid,
547                properties = dict(uid_id = self.tbl_uid.c.id))
548
549     ## Connection functions
550     def __createconn(self):
551         cnf = Config()
552         if cnf["DB::Host"]:
553             # TCP/IP
554             connstr = "postgres://%s" % cnf["DB::Host"]
555             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
556                 connstr += ":%s" % cnf["DB::Port"]
557             connstr += "/%s" % cnf["DB::Name"]
558         else:
559             # Unix Socket
560             connstr = "postgres:///%s" % cnf["DB::Name"]
561             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
562                 connstr += "?port=%s" % cnf["DB::Port"]
563
564         self.db_pg   = create_engine(connstr)
565         self.db_meta = MetaData()
566         self.db_meta.bind = self.db_pg
567         self.db_smaker = sessionmaker(bind=self.db_pg,
568                                       autoflush=True,
569                                       transactional=True)
570
571         self.__setuptables()
572
573     def session(self):
574         return self.db_smaker()
575
576     ## Cache functions
577     def __init_caches(self):
578         self.caches = {'suite':         Cache(),
579                        'section':       Cache(),
580                        'priority':      Cache(),
581                        'override_type': Cache(),
582                        'architecture':  Cache(),
583                        'archive':       Cache(),
584                        'component':     Cache(),
585                        'content_path_names':     Cache(),
586                        'content_file_names':     Cache(),
587                        'location':      Cache(lambda x: '%s_%s_%s' % (x['location'], x['component'], x['location'])),
588                        'maintainer':    {}, # TODO
589                        'keyring':       {}, # TODO
590                        'source':        Cache(lambda x: '%s_%s_' % (x['source'], x['version'])),
591                        'files':         Cache(lambda x: '%s_%s_' % (x['filename'], x['location'])),
592                        'maintainer':    {}, # TODO
593                        'fingerprint':   {}, # TODO
594                        'queue':         {}, # TODO
595                        'uid':           {}, # TODO
596                        'suite_version': Cache(lambda x: '%s_%s' % (x['source'], x['suite'])),
597                       }
598
599         self.prepared_statements = {}
600
601     def prepare(self,name,statement):
602         if not self.prepared_statements.has_key(name):
603             pgc.execute(statement)
604             self.prepared_statements[name] = statement
605
606     def clear_caches(self):
607         self.__init_caches()
608
609     ## Get functions
610     def __get_id(self, retfield, selectobj, cachekey, cachename=None):
611         # This is a bit of a hack but it's an internal function only
612         if cachename is not None:
613             res = self.caches[cachename].GetValue(cachekey)
614             if res:
615                 return res
616
617         c = selectobj.execute()
618
619         if c.rowcount != 1:
620             return None
621
622         res = c.fetchone()
623
624         if retfield not in res.keys():
625             return None
626
627         res = res[retfield]
628
629         if cachename is not None:
630             self.caches[cachename].SetValue(cachekey, res)
631
632         return res
633
634     def get_suite_id(self, suite):
635         """
636         Returns database id for given C{suite}.
637         Results are kept in a cache during runtime to minimize database queries.
638
639         @type suite: string
640         @param suite: The name of the suite
641
642         @rtype: int
643         @return: the database id for the given suite
644
645         """
646         return int(self.__get_id('id',
647                                  self.tbl_suite.select(self.tbl_suite.columns.suite_name == suite),
648                                  suite,
649                                  'suite'))
650
651     def get_section_id(self, section):
652         """
653         Returns database id for given C{section}.
654         Results are kept in a cache during runtime to minimize database queries.
655
656         @type section: string
657         @param section: The name of the section
658
659         @rtype: int
660         @return: the database id for the given section
661
662         """
663         return self.__get_id('id',
664                              self.tbl_section.select(self.tbl_section.columns.section == section),
665                              section,
666                              'section')
667
668     def get_priority_id(self, priority):
669         """
670         Returns database id for given C{priority}.
671         Results are kept in a cache during runtime to minimize database queries.
672
673         @type priority: string
674         @param priority: The name of the priority
675
676         @rtype: int
677         @return: the database id for the given priority
678
679         """
680         return self.__get_id('id',
681                              self.tbl_priority.select(self.tbl_priority.columns.priority == priority),
682                              priority,
683                              'priority')
684
685     def get_override_type_id(self, override_type):
686         """
687         Returns database id for given override C{type}.
688         Results are kept in a cache during runtime to minimize database queries.
689
690         @type override_type: string
691         @param override_type: The name of the override type
692
693         @rtype: int
694         @return: the database id for the given override type
695
696         """
697         return self.__get_id('id',
698                              self.tbl_override_type.select(self.tbl_override_type.columns.type == override_type),
699                              override_type,
700                              'override_type')
701
702     def get_architecture_id(self, architecture):
703         """
704         Returns database id for given C{architecture}.
705         Results are kept in a cache during runtime to minimize database queries.
706
707         @type architecture: string
708         @param architecture: The name of the override type
709
710         @rtype: int
711         @return: the database id for the given architecture
712
713         """
714         return self.__get_id('id',
715                              self.tbl_architecture.select(self.tbl_architecture.columns.arch_string == architecture),
716                              architecture,
717                              'architecture')
718
719     def get_archive_id(self, archive):
720         """
721         returns database id for given c{archive}.
722         results are kept in a cache during runtime to minimize database queries.
723
724         @type archive: string
725         @param archive: the name of the override type
726
727         @rtype: int
728         @return: the database id for the given archive
729
730         """
731         archive = archive.lower()
732         return self.__get_id('id',
733                              self.tbl_archive.select(self.tbl_archive.columns.name == archive),
734                              archive,
735                              'archive')
736
737     def get_component_id(self, component):
738         """
739         Returns database id for given C{component}.
740         Results are kept in a cache during runtime to minimize database queries.
741
742         @type component: string
743         @param component: The name of the override type
744
745         @rtype: int
746         @return: the database id for the given component
747
748         """
749         component = component.lower()
750         return self.__get_id('id',
751                              self.tbl_component.select(self.tbl_component.columns.name == component),
752                              component.lower(),
753                              'component')
754
755     def get_location_id(self, location, component, archive):
756         """
757         Returns database id for the location behind the given combination of
758           - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
759           - B{component} - the id of the component as returned by L{get_component_id}
760           - B{archive} - the id of the archive as returned by L{get_archive_id}
761         Results are kept in a cache during runtime to minimize database queries.
762
763         @type location: string
764         @param location: the path of the location
765
766         @type component: string
767         @param component: the name of the component
768
769         @type archive: string
770         @param archive: the name of the archive
771
772         @rtype: int
773         @return: the database id for the location
774
775         """
776
777         archive = archive.lower()
778         component = component.lower()
779
780         values = {'archive': archive, 'location': location, 'component': component}
781
782         s = self.tbl_location.join(self.tbl_archive).join(self.tbl_component)
783
784         s = s.select(self.tbl_location.columns.path == location)
785         s = s.where(self.tbl_archive.columns.name == archive)
786         s = s.where(self.tbl_component.columns.name == component)
787
788         return self.__get_id('location.id', s, values, 'location')
789
790     def get_source_id(self, source, version):
791         """
792         Returns database id for the combination of C{source} and C{version}
793           - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
794           - B{version}
795         Results are kept in a cache during runtime to minimize database queries.
796
797         @type source: string
798         @param source: source package name
799
800         @type version: string
801         @param version: the source version
802
803         @rtype: int
804         @return: the database id for the source
805
806         """
807         s = self.tbl_source.select()
808         s = s.where(self.tbl_source.columns.source  == source)
809         s = s.where(self.tbl_source.columns.version == version)
810
811         return self.__get_id('id', s, {'source': source, 'version': version}, 'source')
812
813     def get_suite(self, suite):
814         if isinstance(suite, str):
815             suite_id = self.get_suite_id(suite.lower())
816         elif type(suite) == int:
817             suite_id = suite
818
819         s = self.tbl_suite.select(self.tbl_suite.columns.id == suite_id)
820         c = s.execute()
821         if c.rowcount < 1:
822             return None
823         else:
824             return c.fetchone()
825
826     def get_suite_version(self, source, suite):
827         """
828         Returns database id for a combination of C{source} and C{suite}.
829
830           - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
831           - B{suite} - a suite name, eg. I{unstable}
832
833         Results are kept in a cache during runtime to minimize database queries.
834
835         @type source: string
836         @param source: source package name
837
838         @type suite: string
839         @param suite: the suite name
840
841         @rtype: string
842         @return: the version for I{source} in I{suite}
843
844         """
845         s = select([self.tbl_source.columns.source, self.tbl_source.columns.version])
846 #        s = self.tbl_source.join(self.tbl_src_associations).join(self.tbl_suite)
847
848         s = s.select(self.tbl_suite.columns.suite_name == suite, use_labels=True)
849         s = s.select(self.tbl_source.columns.source == source)
850
851         return self.__get_id('source.version', s, {'suite': suite, 'source': source}, 'suite_version')
852
853
854     def get_files_id (self, filename, size, md5sum, location_id):
855         """
856         Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
857         existing copy.
858
859         The database is queried using the C{filename} and C{location_id}. If a file does exist
860         at that location, the existing size and md5sum are checked against the provided
861         parameters. A size or checksum mismatch returns -2. If more than one entry is
862         found within the database, a -1 is returned, no result returns None, otherwise
863         the file id.
864
865         Results are kept in a cache during runtime to minimize database queries.
866
867         @type filename: string
868         @param filename: the filename of the file to check against the DB
869
870         @type size: int
871         @param size: the size of the file to check against the DB
872
873         @type md5sum: string
874         @param md5sum: the md5sum of the file to check against the DB
875
876         @type location_id: int
877         @param location_id: the id of the location as returned by L{get_location_id}
878
879         @rtype: int / None
880         @return: Various return values are possible:
881                    - -2: size/checksum error
882                    - -1: more than one file found in database
883                    - None: no file found in database
884                    - int: file id
885
886         """
887         values = {'filename' : filename,
888                   'location' : location_id}
889
890         res = self.caches['files'].GetValue( values )
891
892         if not res:
893             query = """SELECT id, size, md5sum
894                        FROM files
895                        WHERE filename = %(filename)s AND location = %(location)s"""
896
897             cursor = self.db_con.cursor()
898             cursor.execute( query, values )
899
900             if cursor.rowcount == 0:
901                 res = None
902
903             elif cursor.rowcount != 1:
904                 res = -1
905
906             else:
907                 row = cursor.fetchone()
908
909                 if row[1] != int(size) or row[2] != md5sum:
910                     res =  -2
911
912                 else:
913                     self.caches['files'].SetValue(values, row[0])
914                     res = row[0]
915
916         return res
917
918
919     def get_or_set_contents_file_id(self, filename):
920         """
921         Returns database id for given filename.
922
923         Results are kept in a cache during runtime to minimize database queries.
924         If no matching file is found, a row is inserted.
925
926         @type filename: string
927         @param filename: The filename
928
929         @rtype: int
930         @return: the database id for the given component
931         """
932         try:
933             values={'value': filename}
934             query = "SELECT id FROM content_file_names WHERE file = %(value)s"
935             id = self.__get_single_id(query, values, cachename='content_file_names')
936             if not id:
937                 c = self.db_con.cursor()
938                 c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id",
939                            values )
940
941                 id = c.fetchone()[0]
942                 self.caches['content_file_names'].SetValue(values, id)
943
944             return id
945         except:
946             traceback.print_exc()
947             raise
948
949     def get_or_set_contents_path_id(self, path):
950         """
951         Returns database id for given path.
952
953         Results are kept in a cache during runtime to minimize database queries.
954         If no matching file is found, a row is inserted.
955
956         @type path: string
957         @param path: The filename
958
959         @rtype: int
960         @return: the database id for the given component
961         """
962         try:
963             values={'value': path}
964             query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
965             id = self.__get_single_id(query, values, cachename='content_path_names')
966             if not id:
967                 c = self.db_con.cursor()
968                 c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id",
969                            values )
970
971                 id = c.fetchone()[0]
972                 self.caches['content_path_names'].SetValue(values, id)
973
974             return id
975         except:
976             traceback.print_exc()
977             raise
978
979     def get_suite_architectures(self, suite):
980         """
981         Returns list of architectures for C{suite}.
982
983         @type suite: string, int
984         @param suite: the suite name or the suite_id
985
986         @rtype: list
987         @return: the list of architectures for I{suite}
988         """
989
990         suite_id = None
991         if type(suite) == str:
992             suite_id = self.get_suite_id(suite)
993         elif type(suite) == int:
994             suite_id = suite
995         else:
996             return None
997
998         c = self.db_con.cursor()
999         c.execute( """SELECT a.arch_string FROM suite_architectures sa
1000                       JOIN architecture a ON (a.id = sa.architecture)
1001                       WHERE suite='%s'""" % suite_id )
1002
1003         return map(lambda x: x[0], c.fetchall())
1004
1005     def insert_content_paths(self, bin_id, fullpaths):
1006         """
1007         Make sure given path is associated with given binary id
1008
1009         @type bin_id: int
1010         @param bin_id: the id of the binary
1011         @type fullpaths: list
1012         @param fullpaths: the list of paths of the file being associated with the binary
1013
1014         @return: True upon success
1015         """
1016
1017         c = self.db_con.cursor()
1018
1019         c.execute("BEGIN WORK")
1020         try:
1021
1022             for fullpath in fullpaths:
1023                 (path, file) = os.path.split(fullpath)
1024
1025                 if path.startswith( "./" ):
1026                     path = path[2:]
1027                 # Get the necessary IDs ...
1028                 file_id = self.get_or_set_contents_file_id(file)
1029                 path_id = self.get_or_set_contents_path_id(path)
1030
1031                 c.execute("""INSERT INTO content_associations
1032                                (binary_pkg, filepath, filename)
1033                            VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )
1034
1035             c.execute("COMMIT")
1036             return True
1037         except:
1038             traceback.print_exc()
1039             c.execute("ROLLBACK")
1040             return False
1041
1042     def insert_pending_content_paths(self, package, fullpaths):
1043         """
1044         Make sure given paths are temporarily associated with given
1045         package
1046
1047         @type package: dict
1048         @param package: the package to associate with should have been read in from the binary control file
1049         @type fullpaths: list
1050         @param fullpaths: the list of paths of the file being associated with the binary
1051
1052         @return: True upon success
1053         """
1054
1055         c = self.db_con.cursor()
1056
1057         c.execute("BEGIN WORK")
1058         try:
1059             arch_id = self.get_architecture_id(package['Architecture'])
1060
1061             # Remove any already existing recorded files for this package
1062             c.execute("""DELETE FROM pending_content_associations
1063                          WHERE package=%(Package)s
1064                          AND version=%(Version)s
1065                          AND architecture=%(ArchID)s""", {'Package': package['Package'],
1066                                                           'Version': package['Version'],
1067                                                           'ArchID':  arch_id})
1068
1069             for fullpath in fullpaths:
1070                 (path, file) = os.path.split(fullpath)
1071
1072                 if path.startswith( "./" ):
1073                     path = path[2:]
1074                 # Get the necessary IDs ...
1075                 file_id = self.get_or_set_contents_file_id(file)
1076                 path_id = self.get_or_set_contents_path_id(path)
1077
1078                 c.execute("""INSERT INTO pending_content_associations
1079                                (package, version, architecture, filepath, filename)
1080                             VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
1081                     % (arch_id, path_id, file_id), package )
1082
1083             c.execute("COMMIT")
1084             return True
1085         except:
1086             traceback.print_exc()
1087             c.execute("ROLLBACK")
1088             return False
1089